diff --git a/build-logic/src/main/kotlin/buildlogic.adapter.gradle.kts b/build-logic/src/main/kotlin/buildlogic.adapter.gradle.kts index 41086ee059..0294613de1 100644 --- a/build-logic/src/main/kotlin/buildlogic.adapter.gradle.kts +++ b/build-logic/src/main/kotlin/buildlogic.adapter.gradle.kts @@ -36,6 +36,10 @@ repositories { includeModule("io.papermc.paper", "dev-bundle") } } + maven { + name = "TCodedReleases" + url = uri("https://repo.tcoded.com/releases") + } mavenCentral() afterEvaluate { killNonEngineHubRepositories() @@ -44,6 +48,7 @@ repositories { dependencies { implementation(project(":worldedit-bukkit")) + compileOnly("com.tcoded:FoliaLib:0.5.1") constraints { //Reduces the amount of libraries Gradle and IntelliJ need to resolve implementation("net.kyori:adventure-bom") { diff --git a/build-logic/src/main/kotlin/repositoriesHelper.kt b/build-logic/src/main/kotlin/repositoriesHelper.kt index 11d5c008c2..b1858ed8eb 100644 --- a/build-logic/src/main/kotlin/repositoriesHelper.kt +++ b/build-logic/src/main/kotlin/repositoriesHelper.kt @@ -11,6 +11,7 @@ private val ALLOWED_PREFIXES = listOf( "https://repo.maven.apache.org/maven2/", "https://s01.oss.sonatype.org/content/repositories/snapshots/", "https://plugins.gradle.org", + "https://repo.tcoded.com", "file:" ) private val LOGGER = Logging.getLogger("repositoriesHelper") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9309d7d81a..7884ed90c1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,6 +23,7 @@ parallelgzip = "1.0.5" adventure = "4.26.1" adventure-bukkit = "4.4.1" checkerqual = "3.52.1" +foliaLib = "0.5.1" truezip = "6.8.4" auto-value = "1.11.1" jsr305 = "3.0.2" @@ -119,6 +120,7 @@ paster = { group = "com.intellectualsites.paster", name = "Paster", version.ref vault = { group = "com.github.MilkBowl", name = "VaultAPI", version.ref = "vault" } serverlib = { group = "dev.notmyfault.serverlib", name = "ServerLib", version.ref = "serverlib" } checkerqual = { group = "org.checkerframework", name = "checker-qual", version.ref = "checkerqual" } +foliaLib = { group = "com.tcoded", name = "FoliaLib", version.ref = "foliaLib" } autoService = { group = "com.google.auto.service", name = "auto-service", version.ref = "autoService" } diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java index e524fd0459..b9ae36d39e 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java @@ -10,6 +10,7 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; @@ -21,6 +22,7 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.regen.PaperweightRegen; @@ -127,6 +129,7 @@ import java.util.Objects; import java.util.OptionalInt; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -141,7 +144,8 @@ public final class PaperweightFaweAdapter extends FaweAdapter blockEntityToCompoundTag() { return blockEntity -> FaweCompoundTag.of( - () -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId()) - ); + () -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId())); } @Nullable @@ -205,17 +208,16 @@ private synchronized boolean init() { if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { property = new BooleanProperty( state.getName(), - (List) ImmutableList.copyOf(state.getPossibleValues()) - ); + (List) ImmutableList.copyOf(state.getPossibleValues())); } else if (state instanceof DirectionProperty) { property = new DirectionalProperty( state.getName(), state .getPossibleValues() .stream() - .map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase())) - .collect(Collectors.toList()) - ); + .map(e -> Direction + .valueOf(((StringRepresentable) e).getSerializedName().toUpperCase())) + .collect(Collectors.toList())); } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { property = new EnumProperty( state.getName(), @@ -223,13 +225,11 @@ private synchronized boolean init() { .getPossibleValues() .stream() .map(e -> ((StringRepresentable) e).getSerializedName()) - .collect(Collectors.toList()) - ); + .collect(Collectors.toList())); } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { property = new IntegerProperty( state.getName(), - (List) ImmutableList.copyOf(state.getPossibleValues()) - ); + (List) ImmutableList.copyOf(state.getPossibleValues())); } else { throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + state .getClass() @@ -270,7 +270,8 @@ public BlockMaterial getMaterial(BlockType blockType) { @Override public synchronized BlockMaterial getMaterial(BlockState state) { - net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState(); + net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit + .createBlockData(state.getAsString())).getState(); return new PaperweightBlockMaterial(blockState.getBlock(), blockState); } @@ -333,8 +334,7 @@ public BaseBlock getFullBlock(final Location location) { SideEffect.HISTORY, SideEffect.HEIGHTMAPS, SideEffect.LIGHTING, - SideEffect.NEIGHBORS - ); + SideEffect.NEIGHBORS); @Override public Set getSupportedSideEffects() { @@ -354,24 +354,20 @@ public BaseEntity getEntity(org.bukkit.entity.Entity entity) { Entity mcEntity = craftEntity.getHandle(); String id = getEntityId(mcEntity); + EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id); + Supplier saveTag = () -> { + final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag(); + if (!readEntityIntoTag(mcEntity, minecraftTag)) { + return null; + } + // add Id for AbstractChangeSet to work + final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag); + final Map> tags = NbtUtils.getLinCompoundTagValues(tag); + tags.put("Id", LinStringTag.of(id)); + return LinCompoundTag.of(tags); + }; + return new LazyBaseEntity(type, saveTag); - if (id != null) { - EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id); - Supplier saveTag = () -> { - final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag(); - if (!readEntityIntoTag(mcEntity, minecraftTag)) { - return null; - } - //add Id for AbstractChangeSet to work - final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag); - final Map> tags = NbtUtils.getLinCompoundTagValues(tag); - tags.put("Id", LinStringTag.of(id)); - return LinCompoundTag.of(tags); - }; - return new LazyBaseEntity(type, saveTag); - } else { - return null; - } } @Override @@ -421,8 +417,7 @@ public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockSt return (char) ibdToOrdinal[id]; } catch (ArrayIndexOutOfBoundsException e1) { LOGGER.error("Attempted to convert {} with ID {} to char. ibdToOrdinal length: {}. Defaulting to air!", - blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1 - ); + blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1); return BlockTypesCache.ReservedIDs.AIR; } } @@ -495,18 +490,22 @@ public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockSt @Override public void sendFakeChunk(World world, Player player, ChunkPacket chunkPacket) { ServerLevel nmsWorld = getServerLevel(world); - ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); + ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), + chunkPacket.getChunkZ()); if (map != null && wasAccessibleSinceLastSave(map)) { boolean flag = false; // PlayerChunk.d players = map.players; - Stream stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag) - */ Stream.empty(); + Stream stream = /* + * players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), + * flag) + */ Stream.empty(); ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle(); stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer) .forEach(entityPlayer -> { synchronized (chunkPacket) { - ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket.getNativePacket(); + ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket + .getNativePacket(); if (nmsPacket == null) { nmsPacket = mapUtil.create(this, chunkPacket); chunkPacket.setNativePacket(nmsPacket); @@ -533,8 +532,7 @@ public boolean canPlaceAt(World world, BlockVector3 blockVector3, BlockState blo net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); return blockState1.hasPostProcess( getServerLevel(world), - new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z()) - ); + new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z())); } @Override @@ -542,8 +540,7 @@ public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) { ItemStack stack = new ItemStack( DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM) .get(ResourceLocation.tryParse(baseItemStack.getType().id())), - baseItemStack.getAmount() - ); + baseItemStack.getAmount()); stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(baseItemStack.getNbtData()))); return CraftItemStack.asCraftMirror(stack); } @@ -571,9 +568,22 @@ protected ServerLevel getServerLevel(final World world) { return ((CraftWorld) world).getHandle(); } + private T syncRegion(World world, BlockVector3 pt, java.util.function.Supplier supplier) { + if (FoliaLibHolder.isFolia()) { + Location location = new Location(world, pt.x(), pt.y(), pt.z()); + CompletableFuture future = new CompletableFuture<>(); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> future.complete(supplier.get())); + return future.join(); + } + return TaskManager.taskManager().sync(supplier); + } + @Override - public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, BlockVector3 pt) { - //FAWE start + public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, + BlockVector3 pt) { + // FAWE start ServerLevel serverLevel = getServerLevel(world); ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); @@ -583,15 +593,14 @@ public boolean generateFeature(ConfiguredFeatureType feature, World world, EditS .get(ResourceLocation.tryParse(feature.id())); FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { if (!configuredFeature.place( populator, generator, serverLevel.random, - new BlockPos(pt.x(), pt.y(), pt.z()) - )) { + new BlockPos(pt.x(), pt.y(), pt.z()))) { return null; } List placedBlocks = new ArrayList<>(populator.getList()); @@ -603,7 +612,7 @@ public boolean generateFeature(ConfiguredFeatureType feature, World world, EditS }); return placeFeatureIntoSession(editSession, populator, placed); - //FAWE end + // FAWE end } @Override @@ -621,9 +630,9 @@ public boolean generateStructure(StructureType type, World world, EditSession ed ChunkPos chunkPos = new ChunkPos(new BlockPos(pt.x(), pt.y(), pt.z())); - //FAWE start + // FAWE start FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { StructureStart structureStart = k.generate( @@ -636,20 +645,17 @@ public boolean generateStructure(StructureType type, World world, EditSession ed chunkPos, 0, populator, - biome -> true - ); + biome -> true); if (!structureStart.isValid()) { return null; } else { BoundingBox boundingBox = structureStart.getBoundingBox(); ChunkPos min = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.minX()), - SectionPos.blockToSectionCoord(boundingBox.minZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.minZ())); ChunkPos max = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.maxX()), - SectionPos.blockToSectionCoord(boundingBox.maxZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.maxZ())); ChunkPos.rangeClosed(min, max).forEach((chunkPosx) -> structureStart.placeInChunk( populator, serverLevel.structureManager(), @@ -661,10 +667,8 @@ public boolean generateStructure(StructureType type, World world, EditSession ed chunkPosx.getMinBlockZ(), chunkPosx.getMaxBlockX(), serverLevel.getMaxBuildHeight(), - chunkPosx.getMaxBlockZ() - ), - chunkPosx - )); + chunkPosx.getMaxBlockZ()), + chunkPosx)); List placedBlocks = new ArrayList<>(populator.getList()); placedBlocks.addAll(serverLevel.capturedBlockStates.values()); return placedBlocks; @@ -675,14 +679,13 @@ public boolean generateStructure(StructureType type, World world, EditSession ed }); return placeFeatureIntoSession(editSession, populator, placed); - //FAWE end + // FAWE end } private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, - final List placed - ) { + final List placed) { if (placed == null || placed.isEmpty()) { return false; } @@ -692,7 +695,8 @@ private boolean placeFeatureIntoSession( continue; } BlockPos pos = craftBlockState.getPosition(); - editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), BukkitAdapter.adapt(craftBlockState.getBlockData())); + editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), + BukkitAdapter.adapt(craftBlockState.getBlockData())); BlockEntity blockEntity = populator.getBlockEntity(pos); if (blockEntity != null) { net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId(); @@ -708,7 +712,8 @@ public void setupFeatures() { // All these features should be the "face" selected Set face_features = Arrays - .stream(new Class[]{AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, VegetationFeatures.class}) + .stream(new Class[] { AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, + VegetationFeatures.class }) .flatMap(c -> Arrays.stream(c.getFields())) .filter(f -> { int modifiers = f.getModifiers(); @@ -760,8 +765,10 @@ public void setupFeatures() { @Override public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); - final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount()); - // We should be fine to perform this later as we're using a deep-copied itemstack (above) + final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), + itemStack.getAmount()); + // We should be fine to perform this later as we're using a deep-copied + // itemstack (above) weStack.setNbtReference(LazyReference.from(() -> ((LinCompoundTag) toNativeLin(nmsStack.getTag())))); return weStack; } diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweWorldNativeAccess.java index 352e9b9583..1fce0d1d6a 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweWorldNativeAccess.java @@ -20,6 +20,7 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.chunk.LevelChunk; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData; import org.bukkit.event.block.BlockPhysicsEvent; @@ -58,7 +59,7 @@ public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAd this.level = level; // Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging. // - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway. - this.lastTick = new AtomicInteger(MinecraftServer.currentTick); + this.lastTick = new AtomicInteger(getCurrentTick()); } private Level getLevel() { @@ -94,7 +95,7 @@ public synchronized net.minecraft.world.level.block.state.BlockState setBlockSta LevelChunk levelChunk, BlockPos blockPos, net.minecraft.world.level.block.state.BlockState blockState ) { - int currentTick = MinecraftServer.currentTick; + int currentTick = getCurrentTick(); if (Fawe.isMainThread()) { return levelChunk.setBlockState(blockPos, blockState, this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE) @@ -289,4 +290,16 @@ private record CachedChange( } + private int getCurrentTick() { + try { + return MinecraftServer.currentTick; + } catch (NoSuchFieldError e) { + try { + return Bukkit.getCurrentTick(); + } catch (Exception ex) { + return 0; + } + } + } + } diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java index a16d439ee5..efdea1ba4b 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java @@ -11,6 +11,7 @@ import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; @@ -56,6 +57,8 @@ import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.lighting.LevelLightEngine; import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.World; import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlock; @@ -93,7 +96,8 @@ public class PaperweightGetBlocks extends AbstractBukkitGetBlocks posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); + private static final Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), + v.getZ()); public static final Function NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin .getInstance() .getBukkitImplAdapter()).blockEntityToCompoundTag(); @@ -166,8 +170,9 @@ public BiomeType getBiomeType(int x, int y, int z) { @Override public void removeSectionLighting(int layer, boolean sky) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK) + .getDataLayerData( + sectionPos); if (dataLayer != null) { lightUpdate = true; synchronized (dataLayer) { @@ -194,9 +199,8 @@ public void removeSectionLighting(int layer, boolean sky) { @Override 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))); + BlockEntity blockEntity = getChunk() + .getBlockEntity(new BlockPos((x & 15) + (chunkX << 4), y, (z & 15) + (chunkZ << 4))); if (blockEntity == null) { return null; } @@ -219,19 +223,19 @@ public int getSkyLight(int x, int y, int z) { int alayer = layer - getMinSectionPosition(); if (skyLight[alayer] == null) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = - serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getDataLayerData(sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY) + .getDataLayerData(sectionPos); // If the server hasn't generated the section's NibbleArray yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( LightLayer.BLOCK, sectionPos, - dataLayer - ); + dataLayer); } skyLight[alayer] = dataLayer; } @@ -253,12 +257,13 @@ public int getEmittedLight(int x, int y, int z) { // If the server hasn't generated the section's DataLayer yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); - ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, sectionPos, - dataLayer - ); + ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, + sectionPos, + dataLayer); } blockLight[alayer] = dataLayer; } @@ -331,8 +336,7 @@ protected > T internalCall( Runnable finalizer, int copyKey, LevelChunk nmsChunk, - ServerLevel nmsWorld - ) throws Exception { + ServerLevel nmsWorld) throws Exception { PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null; if (createCopy) { if (copies.containsKey(copyKey)) { @@ -362,7 +366,18 @@ protected > T internalCall( beacons = new ArrayList<>(); } beacons.add(tile); - PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + if (FoliaLibHolder.isFolia()) { + Location location = new Location( + nmsWorld.getWorld(), + tile.getBlockPos().getX(), + tile.getBlockPos().getY(), + tile.getBlockPos().getZ()); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk)); + } else { + PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + } continue; } nmsChunk.removeBlockEntity(tile.getBlockPos()); @@ -399,26 +414,25 @@ protected > T internalCall( } if (existingSection == null) { - PalettedContainer> biomeData = PaperweightPlatformAdapter.getBiomePalettedContainer( - biomes[setSectionIndex], - biomeHolderIdMap - ); + PalettedContainer> biomeData = PaperweightPlatformAdapter + .getBiomePalettedContainer( + biomes[setSectionIndex], + biomeHolderIdMap); LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, new char[4096], adapter, biomeRegistry, - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { - updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex); + getSectionIndex)) { + updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], + getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; @@ -427,8 +441,7 @@ protected > T internalCall( "Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); continue; } } @@ -436,8 +449,7 @@ protected > T internalCall( PalettedContainer> paletteBiomes = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); if (paletteBiomes != null) { PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes); } @@ -449,13 +461,16 @@ protected > T internalCall( bitMask |= 1 << getSectionIndex; - // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to - // this chunk GET when #updateGet is called. Future dords, please listen this time. + // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in + // order to write changes to + // this chunk GET when #updateGet is called. Future dords, please listen this + // time. char[] tmp = set.load(layerNo); char[] setArr = new char[tmp.length]; System.arraycopy(tmp, 0, setArr, 0, tmp.length); - // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was + // synchronise on internal section to avoid circular locking with a continuing + // edit if the chunk was // submitted to keep loaded internal chunks to queue target size. synchronized (super.sectionLocks[getSectionIndex]) { @@ -483,23 +498,22 @@ protected > T internalCall( PalettedContainer> biomeData = biomes == null ? new PalettedContainer<>( biomeHolderIdMap, biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap); + PalettedContainer.Strategy.SECTION_BIOMES) + : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], + biomeHolderIdMap); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, setArr, adapter, biomeRegistry, - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { + getSectionIndex)) { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); continue; } else { @@ -509,14 +523,14 @@ protected > T internalCall( "Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); continue; } } } - //ensure that the server doesn't try to tick the chunksection while we're editing it. (Again) + // ensure that the server doesn't try to tick the chunksection while we're + // editing it. (Again) PaperweightPlatformAdapter.clearCounts(existingSection); if (PaperLib.isPaper()) { existingSection.tickingList.clear(); @@ -537,8 +551,10 @@ protected > T internalCall( this.reset(); } else if (!Arrays.equals(update(getSectionIndex, new char[4096], true), load(layerNo))) { this.reset(layerNo); - /*} else if (lock.isModified()) { - this.reset(layerNo);*/ + /* + * } else if (lock.isModified()) { + * this.reset(layerNo); + */ } } finally { sectionLock.writeLock().unlock(); @@ -547,8 +563,7 @@ protected > T internalCall( PalettedContainer> biomeData = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, @@ -556,22 +571,20 @@ protected > T internalCall( setArr, adapter, biomeRegistry, - biomeData != null ? biomeData : (PalettedContainer>) existingSection.getBiomes() - ); + biomeData != null ? biomeData + : (PalettedContainer>) existingSection.getBiomes()); if (!PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, existingSection, newSection, - getSectionIndex - )) { + getSectionIndex)) { LOGGER.error( "Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); } else { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); } @@ -583,12 +596,12 @@ protected > T internalCall( for (Map.Entry entry : heightMaps.entrySet()) { PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue()); } - PaperweightGetBlocks.this.setLightingToGet(set.getLight(), set.getMinSectionPosition(), set.getMaxSectionPosition()); + PaperweightGetBlocks.this.setLightingToGet(set.getLight(), set.getMinSectionPosition(), + set.getMaxSectionPosition()); PaperweightGetBlocks.this.setSkyLightingToGet( set.getSkyLight(), set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); + set.getMaxSectionPosition()); List syncTasks = new ArrayList<>(); @@ -601,7 +614,8 @@ protected > T internalCall( final List finalBeacons = beacons; syncTasks.add(() -> { for (BlockEntity beacon : finalBeacons) { - BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), SoundEvents.BEACON_DEACTIVATE); + BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), + SoundEvents.BEACON_DEACTIVATE); new BeaconDeactivatedEvent(CraftBlock.at(beacon.getLevel(), beacon.getBlockPos())).callEvent(); } }); @@ -670,17 +684,33 @@ protected > T internalCall( entity.load(tag); entity.absMoveTo(x, y, z, yaw, pitch); entity.setUUID(NbtUtils.uuid(nativeTag)); - if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { - LOGGER.warn( - "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", - id, - nmsWorld.getWorld().getName(), - x, - y, - z - ); - // Unsuccessful create should not be saved to history - iterator.remove(); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } + }); + } else { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } } } } @@ -700,18 +730,38 @@ protected > T internalCall( final int z = blockHash.z() + bz; final BlockPos pos = new BlockPos(x, y, z); - synchronized (nmsWorld) { - BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); - if (tileEntity == null || tileEntity.isRemoved()) { - nmsWorld.removeBlockEntity(pos); - tileEntity = nmsWorld.getBlockEntity(pos); - } - if (tileEntity != null) { - 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)); - tileEntity.load(tag); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + tileEntity.load(tag); + } + } + }); + } else { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + tileEntity.load(tag); + } } } } @@ -733,7 +783,8 @@ protected > T internalCall( // send to player if (!set .getSideEffectSet() - .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) { + .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING + || finalMask == 0 && biomes != null) { this.send(); } if (finalizer != null) { @@ -750,8 +801,7 @@ private void updateGet( LevelChunkSection[] chunkSections, LevelChunkSection section, char[] arr, - int layer - ) { + int layer) { try { sectionLock.writeLock().lock(); if (this.getChunk() != nmsChunk) { @@ -765,8 +815,9 @@ private void updateGet( System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); } if (this.sections[layer] != section) { - // Not sure why it's funky, but it's what I did in commit fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords - this.sections[layer] = new LevelChunkSection[]{section}.clone()[0]; + // Not sure why it's funky, but it's what I did in commit + // fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords + this.sections[layer] = new LevelChunkSection[] { section }.clone()[0]; } } finally { sectionLock.writeLock().unlock(); @@ -782,14 +833,19 @@ public void send() { } /** - * Update a given (nullable) data array to the current data stored in the server's chunk, associated with this - * {@link PaperweightPlatformAdapter} instance. Not synchronised to the {@link PaperweightPlatformAdapter} instance as synchronisation - * is handled where necessary in the method, and should otherwise be handled correctly by this method's caller. + * Update a given (nullable) data array to the current data stored in the + * server's chunk, associated with this + * {@link PaperweightPlatformAdapter} instance. Not synchronised to the + * {@link PaperweightPlatformAdapter} instance as synchronisation + * is handled where necessary in the method, and should otherwise be handled + * correctly by this method's caller. * - * @param layer layer index (0 may denote a negative layer in the world, e.g. at y=-32) + * @param layer layer index (0 may denote a negative layer in the world, + * e.g. at y=-32) * @param data array to be updated/filled with data or null * @param aggressive if the cached section array should be re-acquired. - * @return the given array to be filled with data, or a new array if null is given. + * @return the given array to be filled with data, or a new array if null is + * given. */ @Override @SuppressWarnings("unchecked") @@ -819,7 +875,8 @@ public char[] update(int layer, char[] data, boolean aggressive) { return data; } - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get(dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get(dataObject); final int bitsPerEntry = bits.getBits(); final long[] blockStates = bits.getRaw(); @@ -899,10 +956,10 @@ public LevelChunk getChunk() { } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e); throw new FaweException( - TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), + TextComponent + .of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), FaweException.Type.OTHER, - false - ); + false); } } } @@ -910,14 +967,16 @@ public LevelChunk getChunk() { return levelChunk; } - private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, int maxSectionPosition) { + private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, + int maxSectionPosition) { for (int Y = 0; Y <= maxSectionPosition - minSectionPosition; Y++) { if (light[Y] == null) { continue; } SectionPos sectionPos = SectionPos.of(levelChunk.getPos(), Y + minSectionPosition); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer) + .getDataLayerData( + sectionPos); if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; Arrays.fill(LAYER_COUNT, lightLayer == LightLayer.SKY ? (byte) 15 : (byte) 0); @@ -925,8 +984,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( lightLayer, sectionPos, - dataLayer - ); + dataLayer); } synchronized (dataLayer) { for (int x = 0; x < 16; x++) { @@ -946,8 +1004,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti private PalettedContainer> setBiomesToPalettedContainer( final BiomeType[][] biomes, final int sectionIndex, - final PalettedContainerRO> data - ) { + final PalettedContainerRO> data) { BiomeType[] sectionBiomes; if (biomes == null || (sectionBiomes = biomes[sectionIndex]) == null) { return null; @@ -964,8 +1021,7 @@ private PalettedContainer> setBiomesToPalettedContainer( x, y, z, - biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType)) - ); + biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType))); } } } @@ -1021,8 +1077,9 @@ public boolean trim(boolean aggressive) { final PalettedContainer blocksExisting = existing.getStates(); final Object dataObject = PaperweightPlatformAdapter.fieldData.get(blocksExisting); - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get( - dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get( + dataObject); int paletteSize; if (palette instanceof LinearPalette || palette instanceof HashMapPalette) { @@ -1032,7 +1089,8 @@ public boolean trim(boolean aggressive) { continue; } if (paletteSize == 1) { - //If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks. + // If the cached palette size is 1 then no blocks can have been changed i.e. do + // not need to update these chunks. continue; } super.trim(false, i); diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java index 934060cec7..5069bd5847 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java @@ -8,6 +8,7 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; @@ -130,9 +131,11 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c")); fieldPalette.setAccessible(true); - fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "g")); + fieldTickingFluidCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingFluidCount", "g")); fieldTickingFluidCount.setAccessible(true); - fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); + fieldTickingBlockCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); fieldTickingBlockCount.setAccessible(true); Field tmpFieldBiomes; try { @@ -146,13 +149,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName( "getVisibleChunkIfPresent", - "b" - ), long.class); + "b"), long.class); getVisibleChunkIfPresent.setAccessible(true); methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent); if (!PaperLib.isPaper()) { - fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f")); + fieldThreadingDetector = PalettedContainer.class + .getDeclaredField(Refraction.pickName("threadingDetector", "f")); fieldThreadingDetector.setAccessible(true); fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); fieldLock.setAccessible(true); @@ -165,17 +168,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( Refraction.pickName("removeGameEventListener", "a"), BlockEntity.class, - ServerLevel.class - ); + ServerLevel.class); removeGameEventListener.setAccessible(true); methodRemoveGameEventListener = lookup.unreflect(removeGameEventListener); Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod( Refraction.pickName( "removeBlockEntityTicker", - "l" - ), BlockPos.class - ); + "l"), + BlockPos.class); removeBlockEntityTicker.setAccessible(true); methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker); @@ -199,7 +200,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } try { // Non-Paper - SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "M")); + SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class + .getDeclaredField(Refraction.pickName("entityManager", "M")); SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true); } catch (NoSuchFieldException ignored) { } @@ -207,8 +209,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method palettedContaienrGet = PalettedContainer.class.getDeclaredMethod( Refraction.pickName("get", "a"), - int.class - ); + int.class); palettedContaienrGet.setAccessible(true); PALETTED_CONTAINER_GET = lookup.unreflect(palettedContaienrGet); } catch (RuntimeException | Error e) { @@ -224,14 +225,13 @@ static boolean setSectionAtomic( LevelChunkSection[] sections, LevelChunkSection expected, LevelChunkSection value, - int layer - ) { + int layer) { return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer); } // There is no point in having a functional semaphore for paper servers. - private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = - ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null)); + private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = ThreadLocal + .withInitial(() -> new DelegateSemaphore(1, null)); static DelegateSemaphore applyLock(LevelChunkSection section) { if (PaperLib.isPaper()) { @@ -286,11 +286,11 @@ public static CompletableFuture ensureLoaded(ServerLevel serverLevel "Unexpected error when getting completed future at chunk {},{}. Returning to default.", chunkX, chunkZ, - e - ); + e); } } - return CompletableFuture.supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); + return CompletableFuture + .supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); } private static LevelChunk toLevelChunk(Chunk chunk) { @@ -327,6 +327,14 @@ private static LevelChunk toLevelChunk(Chunk chunk) { } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { + if (FoliaLibHolder.isFolia()) { + try { + serverLevel.getChunkSource().addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), + 0, Unit.INSTANCE); + } catch (Exception ignored) { + } + return; + } // Ensure chunk is definitely loaded before applying a ticket io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel .getChunkSource() @@ -369,7 +377,7 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int if (lockHolder.chunkLock == null) { return; } - MinecraftServer.getServer().execute(() -> { + if (FoliaLibHolder.isFolia()) { try { ClientboundLevelChunkWithLightPacket packet; if (PaperLib.isPaper()) { @@ -386,14 +394,39 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int levelChunk, nmsWorld.getChunkSource().getLightEngine(), null, - null - ); + null); } nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet)); } finally { NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); } - }); + } else { + MinecraftServer.getServer().execute(() -> { + try { + ChunkPos pos = levelChunk.getPos(); + ClientboundLevelChunkWithLightPacket packet; + if (PaperLib.isPaper()) { + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null, + false // last false is to not bother with x-ray + ); + } else { + // deprecated on paper - deprecation suppressed + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null); + } + nearbyPlayers(nmsWorld, pos).forEach(p -> p.connection.send(packet)); + } finally { + NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); + } + }); + } } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { @@ -401,15 +434,14 @@ private static List nearbyPlayers(ServerLevel serverLevel, ChunkPo } /* - NMS conversion + * NMS conversion */ public static LevelChunkSection newChunkSection( final int layer, final char[] blocks, CachedBukkitAdapter adapter, Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { return newChunkSection(layer, null, blocks, adapter, biomeRegistry, biomes); } @@ -419,8 +451,7 @@ public static LevelChunkSection newChunkSection( char[] set, CachedBukkitAdapter adapter, Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (set == null) { return newChunkSection(biomeRegistry, biomes); } @@ -476,15 +507,15 @@ public static LevelChunkSection newChunkSection( } // Create palette with data - @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot - final PalettedContainer blockStatePalettedContainer = - new PalettedContainer<>( - Block.BLOCK_STATE_REGISTRY, - PalettedContainer.Strategy.SECTION_STATES, - PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, bitsPerEntry), - nmsBits, - palette - ); + @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility + // with spigot + final PalettedContainer blockStatePalettedContainer = new PalettedContainer<>( + Block.BLOCK_STATE_REGISTRY, + PalettedContainer.Strategy.SECTION_STATES, + PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, + bitsPerEntry), + nmsBits, + palette); if (biomes == null) { IdMap> biomeHolderIdMap = biomeRegistry.asHolderIdMap(); biomes = new PalettedContainer<>( @@ -494,8 +525,7 @@ public static LevelChunkSection newChunkSection( .getBukkitImplAdapter() .getInternalBiomeId( BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ); + PalettedContainer.Strategy.SECTION_BIOMES); } return new LevelChunkSection(blockStatePalettedContainer, biomes); @@ -512,16 +542,14 @@ public static LevelChunkSection newChunkSection( @SuppressWarnings("deprecation") // Only deprecated in paper private static LevelChunkSection newChunkSection( Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (biomes == null) { return new LevelChunkSection(biomeRegistry); } PalettedContainer dataPaletteBlocks = new PalettedContainer<>( Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), - PalettedContainer.Strategy.SECTION_STATES - ); + PalettedContainer.Strategy.SECTION_STATES); return new LevelChunkSection(dataPaletteBlocks, biomes); } @@ -534,17 +562,18 @@ public static void setBiomesToChunkSection(LevelChunkSection section, PalettedCo } /** - * Create a new {@link PalettedContainer}. Should only be used if no biome container existed beforehand. + * Create a new {@link PalettedContainer}. Should only be used if no + * biome container existed beforehand. */ public static PalettedContainer> getBiomePalettedContainer( BiomeType[] biomes, - IdMap> biomeRegistry - ) { + IdMap> biomeRegistry) { if (biomes == null) { return null; } BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - // Don't stream this as typically will see 1-4 biomes; stream overhead is large for the small length + // Don't stream this as typically will see 1-4 biomes; stream overhead is large + // for the small length Map> palette = new HashMap<>(); for (BiomeType biomeType : new LinkedList<>(Arrays.asList(biomes))) { Holder biome; @@ -559,16 +588,14 @@ public static PalettedContainer> getBiomePalettedContainer( int bitsPerEntry = MathMan.log2nlz(biomeCount - 1); Object configuration = PalettedContainer.Strategy.SECTION_STATES.getConfiguration( new FakeIdMapBiome(biomeCount), - bitsPerEntry - ); + bitsPerEntry); if (bitsPerEntry > 3) { bitsPerEntry = MathMan.log2nlz(biomeRegistry.size() - 1); } PalettedContainer> biomePalettedContainer = new PalettedContainer<>( biomeRegistry, biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ); + PalettedContainer.Strategy.SECTION_BIOMES); final Palette> biomePalette; if (bitsPerEntry == 0) { @@ -603,12 +630,11 @@ public static PalettedContainer> getBiomePalettedContainer( int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes final int arrayLength = MathMan.longArrayLength(bitsPerEntryNonZero, 64); - - BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) : new SimpleBitStorage( - bitsPerEntry, - 64, - new long[arrayLength] - ); + BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) + : new SimpleBitStorage( + bitsPerEntry, + 64, + new long[arrayLength]); try { Object data = dataConstructor.newInstance(configuration, bitStorage, biomePalette); @@ -677,13 +703,13 @@ static List getEntities(LevelChunk chunk) { return Optional.ofNullable(chunk.level .getEntityLookup() .getChunk(chunk.locX, chunk.locZ)).map(c -> { - try { - //noinspection unchecked - return (List) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(c); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException("Failed to lookup entities [PAPER=true]", e); - } - }).orElse(Collections.emptyList()); + try { + // noinspection unchecked + return (List) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(c); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Failed to lookup entities [PAPER=true]", e); + } + }).orElse(Collections.emptyList()); } try { EntityList entityList = (EntityList) LEVEL_CHUNK_ENTITIES.get(chunk); @@ -694,8 +720,9 @@ static List getEntities(LevelChunk chunk) { } } try { - //noinspection unchecked - return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos()); + // noinspection unchecked + return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))) + .getEntities(chunk.getPos()); } catch (IllegalAccessException e) { collector.add(new RuntimeException("Failed to lookup entities [PAPER=false]", e)); } diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/regen/PaperweightRegen.java index e892c6f7e0..7e221d8fec 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/regen/PaperweightRegen.java @@ -1,6 +1,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.regen; import com.fastasyncworldedit.bukkit.adapter.Regenerator; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; @@ -43,6 +44,8 @@ import java.nio.file.Path; import java.util.Map; import java.util.OptionalLong; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.function.BooleanSupplier; import java.util.function.Supplier; @@ -54,14 +57,13 @@ public class PaperweightRegen extends Regenerator { private static final Field paperConfigField; private static final Field generatorSettingBaseSupplierField; - static { try { serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField.setAccessible(true); Field tmpPaperConfigField; - try { //only present on paper + try { // only present on paper tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField.setAccessible(true); } catch (Exception e) { @@ -77,7 +79,7 @@ public class PaperweightRegen extends Regenerator { } } - //runtime + // runtime private ServerLevel originalServerWorld; private ServerLevel freshWorld; private LevelStorageSource.LevelStorageAccess session; @@ -88,8 +90,7 @@ public PaperweightRegen( World originalBukkitWorld, Region region, Extent target, - RegenOptions options - ) { + RegenOptions options) { super(originalBukkitWorld, region, target, options); } @@ -111,10 +112,10 @@ protected boolean prepare() { @Override protected boolean initNewWorld() throws Exception { - //world folder + // world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); - //prepare for world init (see upstream implementation for reference) + // prepare for world init (see upstream implementation for reference) org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment(); org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator(); LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir); @@ -134,21 +135,19 @@ protected boolean initNewWorld() throws Exception { originalWorldData.settings.difficulty(), originalWorldData.settings.allowCommands(), originalWorldData.settings.gameRules(), - originalWorldData.settings.getDataConfiguration() - ); + originalWorldData.settings.getDataConfiguration()); - PrimaryLevelData.SpecialWorldProperty specialWorldProperty = - originalWorldData.isFlatWorld() - ? PrimaryLevelData.SpecialWorldProperty.FLAT - : originalWorldData.isDebugWorld() - ? PrimaryLevelData.SpecialWorldProperty.DEBUG - : PrimaryLevelData.SpecialWorldProperty.NONE; - PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable()); + PrimaryLevelData.SpecialWorldProperty specialWorldProperty = originalWorldData.isFlatWorld() + ? PrimaryLevelData.SpecialWorldProperty.FLAT + : originalWorldData.isDebugWorld() + ? PrimaryLevelData.SpecialWorldProperty.DEBUG + : PrimaryLevelData.SpecialWorldProperty.NONE; + PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, + Lifecycle.stable()); BiomeProvider biomeProvider = getBiomeProvider(); - - //init world + // init world freshWorld = Fawe.instance().getQueueHandler().sync((Supplier) () -> new ServerLevel( server, server.executor, @@ -157,8 +156,7 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.dimension(), new LevelStem( originalServerWorld.dimensionTypeRegistration(), - originalServerWorld.getChunkSource().getGenerator() - ), + originalServerWorld.getChunkSource().getGenerator()), new RegenNoOpWorldLoadListener(), originalServerWorld.isDebug(), seed, @@ -167,13 +165,14 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.getRandomSequences(), environment, generator, - biomeProvider - ) { + biomeProvider) { - private final Holder singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess() - .registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow( - WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) - ) : null; + private final Holder singleBiome = options.hasBiomeType() + ? DedicatedServer.getServer().registryAccess() + .registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow( + WorldEditPlugin.getInstance().getBukkitImplAdapter() + .getInternalBiomeId(options.getBiomeType())) + : null; @Override public @NotNull Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { @@ -187,8 +186,7 @@ protected boolean initNewWorld() throws Exception { public void save( @org.jetbrains.annotations.Nullable final ProgressListener progressListener, final boolean flush, - final boolean savingDisabled - ) { + final boolean savingDisabled) { // noop, spigot } @@ -197,20 +195,61 @@ public void save( @Nullable final ProgressListener progressListener, final boolean flush, final boolean savingDisabled, - final boolean close - ) { + final boolean close) { // noop, paper } }).get(); freshWorld.noSave = true; removeWorldFromWorldsMap(); - newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name + newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); // rename to original world name if (paperConfigField != null) { paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); } + + if (FoliaLibHolder.isFolia()) { + return initWorldForFolia(newWorldData); + } + return true; } + private boolean initWorldForFolia(PrimaryLevelData worldData) throws ExecutionException, InterruptedException { + MinecraftServer console = ((CraftServer) Bukkit.getServer()).getServer(); + + ChunkPos spawnChunk = new ChunkPos( + freshWorld.getChunkSource().randomState().sampler().findSpawnPosition()); + + setRandomSpawnSelection(spawnChunk); + + CompletableFuture initFuture = new CompletableFuture<>(); + + org.bukkit.Location spawnLocation = new org.bukkit.Location( + freshWorld.getWorld(), + spawnChunk.x << 4, + 64, + spawnChunk.z << 4); + FoliaLibHolder.getScheduler().runAtLocation(spawnLocation, task -> { + try { + console.initWorld(freshWorld, worldData, worldData, worldData.worldGenOptions()); + initFuture.complete(true); + } catch (Exception e) { + initFuture.completeExceptionally(e); + } + }); + + return initFuture.get(); + } + + private void setRandomSpawnSelection(ChunkPos spawnChunk) { + try { + Field randomSpawnField = ServerLevel.class.getDeclaredField("randomSpawnSelection"); + randomSpawnField.setAccessible(true); + randomSpawnField.set(freshWorld, spawnChunk); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to set randomSpawnSelection for Folia world initialization", e); + } + } + @Override protected void cleanup() { try { @@ -218,7 +257,7 @@ protected void cleanup() { } catch (Exception ignored) { } - //shutdown chunk provider + // shutdown chunk provider try { Fawe.instance().getQueueHandler().sync(() -> { try { @@ -231,29 +270,35 @@ protected void cleanup() { } catch (Exception ignored) { } - //remove world from server + // remove world from server try { Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap); } catch (Exception ignored) { } - //delete directory + // delete directory try { SafeFiles.tryHardToDeleteDir(tempDir); } catch (Exception ignored) { } } + @Override + protected World getFreshWorld() { + return freshWorld != null ? freshWorld.getWorld() : null; + } + @Override protected IChunkCache initSourceQueueCache() { return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } - //util + // util @SuppressWarnings("unchecked") private void removeWorldFromWorldsMap() { try { - Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + Map map = (Map) serverWorldsField + .get(Bukkit.getServer()); map.remove("faweregentempworld"); } catch (IllegalAccessException e) { throw new RuntimeException(e); @@ -280,8 +325,7 @@ public void updateSpawnPos(@NotNull ChunkPos spawnPos) { @Override public void onStatusChange( final @NotNull ChunkPos pos, - @org.jetbrains.annotations.Nullable final ChunkStatus status - ) { + @org.jetbrains.annotations.Nullable final ChunkStatus status) { } diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java index d642c06b80..4b32d6adb8 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java @@ -10,6 +10,7 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; @@ -21,6 +22,7 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.regen.PaperweightRegen; @@ -126,6 +128,7 @@ import java.util.Objects; import java.util.OptionalInt; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -140,7 +143,8 @@ public final class PaperweightFaweAdapter extends FaweAdapter blockEntityToCompoundTag() { return blockEntity -> FaweCompoundTag.of( - () -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId()) - ); + () -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId())); } @Nullable @@ -204,17 +207,16 @@ private synchronized boolean init() { if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { property = new BooleanProperty( state.getName(), - (List) ImmutableList.copyOf(state.getPossibleValues()) - ); + (List) ImmutableList.copyOf(state.getPossibleValues())); } else if (state instanceof DirectionProperty) { property = new DirectionalProperty( state.getName(), state .getPossibleValues() .stream() - .map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase())) - .collect(Collectors.toList()) - ); + .map(e -> Direction + .valueOf(((StringRepresentable) e).getSerializedName().toUpperCase())) + .collect(Collectors.toList())); } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { property = new EnumProperty( state.getName(), @@ -222,13 +224,11 @@ private synchronized boolean init() { .getPossibleValues() .stream() .map(e -> ((StringRepresentable) e).getSerializedName()) - .collect(Collectors.toList()) - ); + .collect(Collectors.toList())); } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { property = new IntegerProperty( state.getName(), - (List) ImmutableList.copyOf(state.getPossibleValues()) - ); + (List) ImmutableList.copyOf(state.getPossibleValues())); } else { throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + state .getClass() @@ -269,7 +269,8 @@ public BlockMaterial getMaterial(BlockType blockType) { @Override public synchronized BlockMaterial getMaterial(BlockState state) { - net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState(); + net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit + .createBlockData(state.getAsString())).getState(); return new PaperweightBlockMaterial(blockState.getBlock(), blockState); } @@ -332,8 +333,7 @@ public BaseBlock getFullBlock(final Location location) { SideEffect.HISTORY, SideEffect.HEIGHTMAPS, SideEffect.LIGHTING, - SideEffect.NEIGHBORS - ); + SideEffect.NEIGHBORS); @Override public Set getSupportedSideEffects() { @@ -353,24 +353,20 @@ public BaseEntity getEntity(org.bukkit.entity.Entity entity) { Entity mcEntity = craftEntity.getHandle(); String id = getEntityId(mcEntity); + EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id); + Supplier saveTag = () -> { + final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag(); + if (!readEntityIntoTag(mcEntity, minecraftTag)) { + return null; + } + // add Id for AbstractChangeSet to work + final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag); + final Map> tags = NbtUtils.getLinCompoundTagValues(tag); + tags.put("Id", LinStringTag.of(id)); + return LinCompoundTag.of(tags); + }; + return new LazyBaseEntity(type, saveTag); - if (id != null) { - EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id); - Supplier saveTag = () -> { - final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag(); - if (!readEntityIntoTag(mcEntity, minecraftTag)) { - return null; - } - //add Id for AbstractChangeSet to work - final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag); - final Map> tags = NbtUtils.getLinCompoundTagValues(tag); - tags.put("Id", LinStringTag.of(id)); - return LinCompoundTag.of(tags); - }; - return new LazyBaseEntity(type, saveTag); - } else { - return null; - } } @Override @@ -420,8 +416,7 @@ public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockSt return (char) ibdToOrdinal[id]; } catch (ArrayIndexOutOfBoundsException e1) { LOGGER.error("Attempted to convert {} with ID {} to char. ibdToOrdinal length: {}. Defaulting to air!", - blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1 - ); + blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1); return BlockTypesCache.ReservedIDs.AIR; } } @@ -494,18 +489,22 @@ public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockSt @Override public void sendFakeChunk(World world, Player player, ChunkPacket chunkPacket) { ServerLevel nmsWorld = getServerLevel(world); - ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); + ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), + chunkPacket.getChunkZ()); if (map != null && wasAccessibleSinceLastSave(map)) { boolean flag = false; // PlayerChunk.d players = map.players; - Stream stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag) - */ Stream.empty(); + Stream stream = /* + * players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), + * flag) + */ Stream.empty(); ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle(); stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer) .forEach(entityPlayer -> { synchronized (chunkPacket) { - ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket.getNativePacket(); + ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket + .getNativePacket(); if (nmsPacket == null) { nmsPacket = mapUtil.create(this, chunkPacket); chunkPacket.setNativePacket(nmsPacket); @@ -528,11 +527,11 @@ public void sendFakeChunk(World world, Player player, ChunkPacket chunkPacket) { @Override public boolean canPlaceAt(World world, BlockVector3 blockVector3, BlockState blockState) { - net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(getOrdinalToIbdID()[blockState.getOrdinal()]); + net.minecraft.world.level.block.state.BlockState blockState1 = Block + .stateById(getOrdinalToIbdID()[blockState.getOrdinal()]); return blockState1.hasPostProcess( getServerLevel(world), - new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z()) - ); + new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z())); } @Override @@ -540,8 +539,7 @@ public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) { ItemStack stack = new ItemStack( DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM) .get(ResourceLocation.tryParse(baseItemStack.getType().id())), - baseItemStack.getAmount() - ); + baseItemStack.getAmount()); stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(baseItemStack.getNbtData()))); return CraftItemStack.asCraftMirror(stack); } @@ -569,9 +567,22 @@ protected ServerLevel getServerLevel(final World world) { return ((CraftWorld) world).getHandle(); } + private T syncRegion(World world, BlockVector3 pt, java.util.function.Supplier supplier) { + if (FoliaLibHolder.isFolia()) { + Location location = new Location(world, pt.x(), pt.y(), pt.z()); + CompletableFuture future = new CompletableFuture<>(); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> future.complete(supplier.get())); + return future.join(); + } + return TaskManager.taskManager().sync(supplier); + } + @Override - public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, BlockVector3 pt) { - //FAWE start + public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, + BlockVector3 pt) { + // FAWE start ServerLevel serverLevel = getServerLevel(world); ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); @@ -581,15 +592,14 @@ public boolean generateFeature(ConfiguredFeatureType feature, World world, EditS .get(ResourceLocation.tryParse(feature.id())); FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { if (!configuredFeature.place( populator, generator, serverLevel.random, - new BlockPos(pt.x(), pt.y(), pt.z()) - )) { + new BlockPos(pt.x(), pt.y(), pt.z()))) { return null; } @@ -601,7 +611,7 @@ public boolean generateFeature(ConfiguredFeatureType feature, World world, EditS } }); return placeFeatureIntoSession(editSession, populator, placed); - //FAWE end + // FAWE end } @Override @@ -619,9 +629,9 @@ public boolean generateStructure(StructureType type, World world, EditSession ed ChunkPos chunkPos = new ChunkPos(new BlockPos(pt.x(), pt.y(), pt.z())); - //FAWE start + // FAWE start FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { StructureStart structureStart = k.generate( @@ -634,8 +644,7 @@ public boolean generateStructure(StructureType type, World world, EditSession ed chunkPos, 0, populator, - biome -> true - ); + biome -> true); if (!structureStart.isValid()) { return null; @@ -643,12 +652,10 @@ public boolean generateStructure(StructureType type, World world, EditSession ed BoundingBox boundingBox = structureStart.getBoundingBox(); ChunkPos min = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.minX()), - SectionPos.blockToSectionCoord(boundingBox.minZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.minZ())); ChunkPos max = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.maxX()), - SectionPos.blockToSectionCoord(boundingBox.maxZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.maxZ())); ChunkPos.rangeClosed(min, max).forEach((chunkPosx) -> structureStart.placeInChunk( populator, serverLevel.structureManager(), @@ -660,10 +667,8 @@ public boolean generateStructure(StructureType type, World world, EditSession ed chunkPosx.getMinBlockZ(), chunkPosx.getMaxBlockX(), serverLevel.getMaxBuildHeight(), - chunkPosx.getMaxBlockZ() - ), - chunkPosx - )); + chunkPosx.getMaxBlockZ()), + chunkPosx)); List placedBlocks = new ArrayList<>(populator.getList()); placedBlocks.addAll(serverLevel.capturedBlockStates.values()); return placedBlocks; @@ -674,14 +679,13 @@ public boolean generateStructure(StructureType type, World world, EditSession ed }); return placeFeatureIntoSession(editSession, populator, placed); - //FAWE end + // FAWE end } private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, - final List placed - ) { + final List placed) { if (placed == null || placed.isEmpty()) { return false; } @@ -691,7 +695,8 @@ private boolean placeFeatureIntoSession( continue; } BlockPos pos = craftBlockState.getPosition(); - editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), BukkitAdapter.adapt(craftBlockState.getBlockData())); + editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), + BukkitAdapter.adapt(craftBlockState.getBlockData())); BlockEntity blockEntity = populator.getBlockEntity(pos); if (blockEntity != null) { net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId(); @@ -707,7 +712,8 @@ public void setupFeatures() { // All these features should be the "face" selected Set face_features = Arrays - .stream(new Class[]{AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, VegetationFeatures.class}) + .stream(new Class[] { AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, + VegetationFeatures.class }) .flatMap(c -> Arrays.stream(c.getFields())) .filter(f -> { int modifiers = f.getModifiers(); @@ -759,8 +765,10 @@ public void setupFeatures() { @Override public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); - final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount()); - // We should be fine to perform this later as we're using a deep-copied itemstack (above) + final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), + itemStack.getAmount()); + // We should be fine to perform this later as we're using a deep-copied + // itemstack (above) weStack.setNbtReference(LazyReference.from(() -> ((LinCompoundTag) toNativeLin(nmsStack.getTag())))); return weStack; } diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweWorldNativeAccess.java index 4fb3e04851..26131962e5 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweWorldNativeAccess.java @@ -20,6 +20,7 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.chunk.LevelChunk; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData; import org.bukkit.event.block.BlockPhysicsEvent; @@ -58,7 +59,7 @@ public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAd this.level = level; // Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging. // - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway. - this.lastTick = new AtomicInteger(MinecraftServer.currentTick); + this.lastTick = new AtomicInteger(getCurrentTick()); } private Level getLevel() { @@ -94,7 +95,7 @@ public synchronized net.minecraft.world.level.block.state.BlockState setBlockSta LevelChunk levelChunk, BlockPos blockPos, net.minecraft.world.level.block.state.BlockState blockState ) { - int currentTick = MinecraftServer.currentTick; + int currentTick = getCurrentTick(); if (Fawe.isMainThread()) { return levelChunk.setBlockState(blockPos, blockState, this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE) @@ -289,4 +290,17 @@ private record CachedChange( } + + private int getCurrentTick() { + try { + return MinecraftServer.currentTick; + } catch (NoSuchFieldError e) { + try { + return Bukkit.getCurrentTick(); + } catch (Exception ex) { + return 0; + } + } + } + } diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java index 0b1522d7f7..c8466c02e8 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java @@ -11,6 +11,7 @@ import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; @@ -56,6 +57,8 @@ import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.lighting.LevelLightEngine; import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.World; import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlock; @@ -93,7 +96,8 @@ public class PaperweightGetBlocks extends AbstractBukkitGetBlocks posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); + private static final Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), + v.getZ()); public static final Function NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin .getInstance() .getBukkitImplAdapter()).blockEntityToCompoundTag(); @@ -166,8 +170,9 @@ public BiomeType getBiomeType(int x, int y, int z) { @Override public void removeSectionLighting(int layer, boolean sky) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK) + .getDataLayerData( + sectionPos); if (dataLayer != null) { lightUpdate = true; synchronized (dataLayer) { @@ -194,9 +199,8 @@ public void removeSectionLighting(int layer, boolean sky) { @Override 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))); + BlockEntity blockEntity = getChunk() + .getBlockEntity(new BlockPos((x & 15) + (chunkX << 4), y, (z & 15) + (chunkZ << 4))); if (blockEntity == null) { return null; } @@ -219,19 +223,19 @@ public int getSkyLight(int x, int y, int z) { int alayer = layer - getMinSectionPosition(); if (skyLight[alayer] == null) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = - serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getDataLayerData(sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY) + .getDataLayerData(sectionPos); // If the server hasn't generated the section's NibbleArray yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( LightLayer.BLOCK, sectionPos, - dataLayer - ); + dataLayer); } skyLight[alayer] = dataLayer; } @@ -253,12 +257,13 @@ public int getEmittedLight(int x, int y, int z) { // If the server hasn't generated the section's DataLayer yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); - ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, sectionPos, - dataLayer - ); + ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, + sectionPos, + dataLayer); } blockLight[alayer] = dataLayer; } @@ -331,8 +336,7 @@ protected > T internalCall( Runnable finalizer, int copyKey, LevelChunk nmsChunk, - ServerLevel nmsWorld - ) throws Exception { + ServerLevel nmsWorld) throws Exception { PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null; if (createCopy) { if (copies.containsKey(copyKey)) { @@ -362,7 +366,18 @@ protected > T internalCall( beacons = new ArrayList<>(); } beacons.add(tile); - PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + if (FoliaLibHolder.isFolia()) { + Location location = new Location( + nmsWorld.getWorld(), + tile.getBlockPos().getX(), + tile.getBlockPos().getY(), + tile.getBlockPos().getZ()); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk)); + } else { + PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + } continue; } nmsChunk.removeBlockEntity(tile.getBlockPos()); @@ -399,26 +414,25 @@ protected > T internalCall( } if (existingSection == null) { - PalettedContainer> biomeData = PaperweightPlatformAdapter.getBiomePalettedContainer( - biomes[setSectionIndex], - biomeHolderIdMap - ); + PalettedContainer> biomeData = PaperweightPlatformAdapter + .getBiomePalettedContainer( + biomes[setSectionIndex], + biomeHolderIdMap); LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, new char[4096], adapter, biomeRegistry, - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { - updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex); + getSectionIndex)) { + updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], + getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; @@ -427,8 +441,7 @@ protected > T internalCall( "Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); continue; } } @@ -436,8 +449,7 @@ protected > T internalCall( PalettedContainer> paletteBiomes = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); if (paletteBiomes != null) { PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes); } @@ -449,13 +461,16 @@ protected > T internalCall( bitMask |= 1 << getSectionIndex; - // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to - // this chunk GET when #updateGet is called. Future dords, please listen this time. + // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in + // order to write changes to + // this chunk GET when #updateGet is called. Future dords, please listen this + // time. char[] tmp = set.load(layerNo); char[] setArr = new char[tmp.length]; System.arraycopy(tmp, 0, setArr, 0, tmp.length); - // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was + // synchronise on internal section to avoid circular locking with a continuing + // edit if the chunk was // submitted to keep loaded internal chunks to queue target size. synchronized (super.sectionLocks[getSectionIndex]) { @@ -483,23 +498,22 @@ protected > T internalCall( PalettedContainer> biomeData = biomes == null ? new PalettedContainer<>( biomeHolderIdMap, biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap); + PalettedContainer.Strategy.SECTION_BIOMES) + : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], + biomeHolderIdMap); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, setArr, adapter, biomeRegistry, - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { + getSectionIndex)) { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); continue; } else { @@ -509,14 +523,14 @@ protected > T internalCall( "Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); continue; } } } - //ensure that the server doesn't try to tick the chunksection while we're editing it. (Again) + // ensure that the server doesn't try to tick the chunksection while we're + // editing it. (Again) PaperweightPlatformAdapter.clearCounts(existingSection); if (PaperLib.isPaper()) { existingSection.tickingList.clear(); @@ -537,8 +551,10 @@ protected > T internalCall( this.reset(); } else if (!Arrays.equals(update(getSectionIndex, new char[4096], true), load(layerNo))) { this.reset(layerNo); - /*} else if (lock.isModified()) { - this.reset(layerNo);*/ + /* + * } else if (lock.isModified()) { + * this.reset(layerNo); + */ } } finally { sectionLock.writeLock().unlock(); @@ -547,8 +563,7 @@ protected > T internalCall( PalettedContainer> biomeData = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, @@ -556,22 +571,20 @@ protected > T internalCall( setArr, adapter, biomeRegistry, - biomeData != null ? biomeData : (PalettedContainer>) existingSection.getBiomes() - ); + biomeData != null ? biomeData + : (PalettedContainer>) existingSection.getBiomes()); if (!PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, existingSection, newSection, - getSectionIndex - )) { + getSectionIndex)) { LOGGER.error( "Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); } else { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); } @@ -583,12 +596,12 @@ protected > T internalCall( for (Map.Entry entry : heightMaps.entrySet()) { PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue()); } - PaperweightGetBlocks.this.setLightingToGet(set.getLight(), set.getMinSectionPosition(), set.getMaxSectionPosition()); + PaperweightGetBlocks.this.setLightingToGet(set.getLight(), set.getMinSectionPosition(), + set.getMaxSectionPosition()); PaperweightGetBlocks.this.setSkyLightingToGet( set.getSkyLight(), set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); + set.getMaxSectionPosition()); List syncTasks = new ArrayList<>(); @@ -601,7 +614,8 @@ protected > T internalCall( final List finalBeacons = beacons; syncTasks.add(() -> { for (BlockEntity beacon : finalBeacons) { - BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), SoundEvents.BEACON_DEACTIVATE); + BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), + SoundEvents.BEACON_DEACTIVATE); new BeaconDeactivatedEvent(CraftBlock.at(beacon.getLevel(), beacon.getBlockPos())).callEvent(); } }); @@ -663,25 +677,42 @@ protected > T internalCall( if (type != null) { Entity entity = type.create(nmsWorld); if (entity != null) { - final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin( - linTag); + 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(NbtUtils.uuid(nativeTag)); - if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { - LOGGER.warn( - "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", - id, - nmsWorld.getWorld().getName(), - x, - y, - z - ); - // Unsuccessful create should not be saved to history - iterator.remove(); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } + }); + } else { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } } } } @@ -701,18 +732,38 @@ protected > T internalCall( final int z = blockHash.z() + bz; final BlockPos pos = new BlockPos(x, y, z); - synchronized (nmsWorld) { - BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); - if (tileEntity == null || tileEntity.isRemoved()) { - nmsWorld.removeBlockEntity(pos); - tileEntity = nmsWorld.getBlockEntity(pos); - } - if (tileEntity != null) { - 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)); - tileEntity.load(tag); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + tileEntity.load(tag); + } + } + }); + } else { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + tileEntity.load(tag); + } } } } @@ -734,7 +785,8 @@ protected > T internalCall( // send to player if (!set .getSideEffectSet() - .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) { + .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING + || finalMask == 0 && biomes != null) { this.send(); } if (finalizer != null) { @@ -751,8 +803,7 @@ private void updateGet( LevelChunkSection[] chunkSections, LevelChunkSection section, char[] arr, - int layer - ) { + int layer) { try { sectionLock.writeLock().lock(); if (this.getChunk() != nmsChunk) { @@ -766,8 +817,9 @@ private void updateGet( System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); } if (this.sections[layer] != section) { - // Not sure why it's funky, but it's what I did in commit fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords - this.sections[layer] = new LevelChunkSection[]{section}.clone()[0]; + // Not sure why it's funky, but it's what I did in commit + // fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords + this.sections[layer] = new LevelChunkSection[] { section }.clone()[0]; } } finally { sectionLock.writeLock().unlock(); @@ -783,14 +835,19 @@ public void send() { } /** - * Update a given (nullable) data array to the current data stored in the server's chunk, associated with this - * {@link PaperweightPlatformAdapter} instance. Not synchronised to the {@link PaperweightPlatformAdapter} instance as synchronisation - * is handled where necessary in the method, and should otherwise be handled correctly by this method's caller. + * Update a given (nullable) data array to the current data stored in the + * server's chunk, associated with this + * {@link PaperweightPlatformAdapter} instance. Not synchronised to the + * {@link PaperweightPlatformAdapter} instance as synchronisation + * is handled where necessary in the method, and should otherwise be handled + * correctly by this method's caller. * - * @param layer layer index (0 may denote a negative layer in the world, e.g. at y=-32) + * @param layer layer index (0 may denote a negative layer in the world, + * e.g. at y=-32) * @param data array to be updated/filled with data or null * @param aggressive if the cached section array should be re-acquired. - * @return the given array to be filled with data, or a new array if null is given. + * @return the given array to be filled with data, or a new array if null is + * given. */ @Override @SuppressWarnings("unchecked") @@ -820,7 +877,8 @@ public char[] update(int layer, char[] data, boolean aggressive) { return data; } - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get(dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get(dataObject); final int bitsPerEntry = bits.getBits(); final long[] blockStates = bits.getRaw(); @@ -900,10 +958,10 @@ public LevelChunk getChunk() { } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e); throw new FaweException( - TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), + TextComponent + .of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), FaweException.Type.OTHER, - false - ); + false); } } } @@ -911,14 +969,16 @@ public LevelChunk getChunk() { return levelChunk; } - private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, int maxSectionPosition) { + private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, + int maxSectionPosition) { for (int Y = 0; Y <= maxSectionPosition - minSectionPosition; Y++) { if (light[Y] == null) { continue; } SectionPos sectionPos = SectionPos.of(levelChunk.getPos(), Y + minSectionPosition); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer) + .getDataLayerData( + sectionPos); if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; Arrays.fill(LAYER_COUNT, lightLayer == LightLayer.SKY ? (byte) 15 : (byte) 0); @@ -926,8 +986,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( lightLayer, sectionPos, - dataLayer - ); + dataLayer); } synchronized (dataLayer) { for (int x = 0; x < 16; x++) { @@ -947,8 +1006,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti private PalettedContainer> setBiomesToPalettedContainer( final BiomeType[][] biomes, final int sectionIndex, - final PalettedContainerRO> data - ) { + final PalettedContainerRO> data) { BiomeType[] sectionBiomes; if (biomes == null || (sectionBiomes = biomes[sectionIndex]) == null) { return null; @@ -965,8 +1023,7 @@ private PalettedContainer> setBiomesToPalettedContainer( x, y, z, - biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType)) - ); + biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType))); } } } @@ -1022,8 +1079,9 @@ public boolean trim(boolean aggressive) { final PalettedContainer blocksExisting = existing.getStates(); final Object dataObject = PaperweightPlatformAdapter.fieldData.get(blocksExisting); - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get( - dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get( + dataObject); int paletteSize; if (palette instanceof LinearPalette || palette instanceof HashMapPalette) { @@ -1033,7 +1091,8 @@ public boolean trim(boolean aggressive) { continue; } if (paletteSize == 1) { - //If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks. + // If the cached palette size is 1 then no blocks can have been changed i.e. do + // not need to update these chunks. continue; } super.trim(false, i); diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java index 9367612169..7e874fb676 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java @@ -8,6 +8,7 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; @@ -130,9 +131,11 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c")); fieldPalette.setAccessible(true); - fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "g")); + fieldTickingFluidCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingFluidCount", "g")); fieldTickingFluidCount.setAccessible(true); - fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); + fieldTickingBlockCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); fieldTickingBlockCount.setAccessible(true); Field tmpFieldBiomes; try { @@ -146,13 +149,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName( "getVisibleChunkIfPresent", - "b" - ), long.class); + "b"), long.class); getVisibleChunkIfPresent.setAccessible(true); methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent); if (!PaperLib.isPaper()) { - fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f")); + fieldThreadingDetector = PalettedContainer.class + .getDeclaredField(Refraction.pickName("threadingDetector", "f")); fieldThreadingDetector.setAccessible(true); fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); fieldLock.setAccessible(true); @@ -165,17 +168,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( Refraction.pickName("removeGameEventListener", "a"), BlockEntity.class, - ServerLevel.class - ); + ServerLevel.class); removeGameEventListener.setAccessible(true); methodRemoveGameEventListener = lookup.unreflect(removeGameEventListener); Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod( Refraction.pickName( "removeBlockEntityTicker", - "l" - ), BlockPos.class - ); + "l"), + BlockPos.class); removeBlockEntityTicker.setAccessible(true); methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker); @@ -199,7 +200,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } try { // Non-Paper - SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "M")); + SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class + .getDeclaredField(Refraction.pickName("entityManager", "M")); SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true); } catch (NoSuchFieldException ignored) { } @@ -207,8 +209,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method palettedContaienrGet = PalettedContainer.class.getDeclaredMethod( Refraction.pickName("get", "a"), - int.class - ); + int.class); palettedContaienrGet.setAccessible(true); PALETTED_CONTAINER_GET = lookup.unreflect(palettedContaienrGet); } catch (RuntimeException | Error e) { @@ -224,14 +225,13 @@ static boolean setSectionAtomic( LevelChunkSection[] sections, LevelChunkSection expected, LevelChunkSection value, - int layer - ) { + int layer) { return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer); } // There is no point in having a functional semaphore for paper servers. - private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = - ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null)); + private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = ThreadLocal + .withInitial(() -> new DelegateSemaphore(1, null)); static DelegateSemaphore applyLock(LevelChunkSection section) { if (PaperLib.isPaper()) { @@ -286,11 +286,11 @@ public static CompletableFuture ensureLoaded(ServerLevel serverLevel "Unexpected error when getting completed future at chunk {},{}. Returning to default.", chunkX, chunkZ, - e - ); + e); } } - return CompletableFuture.supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); + return CompletableFuture + .supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); } private static LevelChunk toLevelChunk(Chunk chunk) { @@ -327,6 +327,14 @@ private static LevelChunk toLevelChunk(Chunk chunk) { } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { + if (FoliaLibHolder.isFolia()) { + try { + serverLevel.getChunkSource().addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), + 0, Unit.INSTANCE); + } catch (Exception ignored) { + } + return; + } // Ensure chunk is definitely loaded before applying a ticket io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel .getChunkSource() @@ -369,7 +377,7 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int if (lockHolder.chunkLock == null) { return; } - MinecraftServer.getServer().execute(() -> { + if (FoliaLibHolder.isFolia()) { try { ClientboundLevelChunkWithLightPacket packet; if (PaperLib.isPaper()) { @@ -386,14 +394,39 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int levelChunk, nmsWorld.getChunkSource().getLightEngine(), null, - null - ); + null); } nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet)); } finally { NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); } - }); + } else { + MinecraftServer.getServer().execute(() -> { + try { + ChunkPos pos = levelChunk.getPos(); + ClientboundLevelChunkWithLightPacket packet; + if (PaperLib.isPaper()) { + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null, + false // last false is to not bother with x-ray + ); + } else { + // deprecated on paper - deprecation suppressed + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null); + } + nearbyPlayers(nmsWorld, pos).forEach(p -> p.connection.send(packet)); + } finally { + NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); + } + }); + } } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { @@ -401,15 +434,14 @@ private static List nearbyPlayers(ServerLevel serverLevel, ChunkPo } /* - NMS conversion + * NMS conversion */ public static LevelChunkSection newChunkSection( final int layer, final char[] blocks, CachedBukkitAdapter adapter, Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { return newChunkSection(layer, null, blocks, adapter, biomeRegistry, biomes); } @@ -419,8 +451,7 @@ public static LevelChunkSection newChunkSection( char[] set, CachedBukkitAdapter adapter, Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (set == null) { return newChunkSection(biomeRegistry, biomes); } @@ -476,15 +507,15 @@ public static LevelChunkSection newChunkSection( } // Create palette with data - @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot - final PalettedContainer blockStatePalettedContainer = - new PalettedContainer<>( - Block.BLOCK_STATE_REGISTRY, - PalettedContainer.Strategy.SECTION_STATES, - PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, bitsPerEntry), - nmsBits, - palette - ); + @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility + // with spigot + final PalettedContainer blockStatePalettedContainer = new PalettedContainer<>( + Block.BLOCK_STATE_REGISTRY, + PalettedContainer.Strategy.SECTION_STATES, + PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, + bitsPerEntry), + nmsBits, + palette); if (biomes == null) { IdMap> biomeHolderIdMap = biomeRegistry.asHolderIdMap(); biomes = new PalettedContainer<>( @@ -494,8 +525,7 @@ public static LevelChunkSection newChunkSection( .getBukkitImplAdapter() .getInternalBiomeId( BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ); + PalettedContainer.Strategy.SECTION_BIOMES); } return new LevelChunkSection(blockStatePalettedContainer, biomes); @@ -512,16 +542,14 @@ public static LevelChunkSection newChunkSection( @SuppressWarnings("deprecation") // Only deprecated in paper private static LevelChunkSection newChunkSection( Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (biomes == null) { return new LevelChunkSection(biomeRegistry); } PalettedContainer dataPaletteBlocks = new PalettedContainer<>( Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), - PalettedContainer.Strategy.SECTION_STATES - ); + PalettedContainer.Strategy.SECTION_STATES); return new LevelChunkSection(dataPaletteBlocks, biomes); } @@ -534,17 +562,18 @@ public static void setBiomesToChunkSection(LevelChunkSection section, PalettedCo } /** - * Create a new {@link PalettedContainer}. Should only be used if no biome container existed beforehand. + * Create a new {@link PalettedContainer}. Should only be used if no + * biome container existed beforehand. */ public static PalettedContainer> getBiomePalettedContainer( BiomeType[] biomes, - IdMap> biomeRegistry - ) { + IdMap> biomeRegistry) { if (biomes == null) { return null; } BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - // Don't stream this as typically will see 1-4 biomes; stream overhead is large for the small length + // Don't stream this as typically will see 1-4 biomes; stream overhead is large + // for the small length Map> palette = new HashMap<>(); for (BiomeType biomeType : new LinkedList<>(Arrays.asList(biomes))) { Holder biome; @@ -559,16 +588,14 @@ public static PalettedContainer> getBiomePalettedContainer( int bitsPerEntry = MathMan.log2nlz(biomeCount - 1); Object configuration = PalettedContainer.Strategy.SECTION_STATES.getConfiguration( new FakeIdMapBiome(biomeCount), - bitsPerEntry - ); + bitsPerEntry); if (bitsPerEntry > 3) { bitsPerEntry = MathMan.log2nlz(biomeRegistry.size() - 1); } PalettedContainer> biomePalettedContainer = new PalettedContainer<>( biomeRegistry, biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ); + PalettedContainer.Strategy.SECTION_BIOMES); final Palette> biomePalette; if (bitsPerEntry == 0) { @@ -603,12 +630,11 @@ public static PalettedContainer> getBiomePalettedContainer( int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes final int arrayLength = MathMan.longArrayLength(bitsPerEntryNonZero, 64); - - BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) : new SimpleBitStorage( - bitsPerEntry, - 64, - new long[arrayLength] - ); + BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) + : new SimpleBitStorage( + bitsPerEntry, + 64, + new long[arrayLength]); try { Object data = dataConstructor.newInstance(configuration, bitStorage, biomePalette); @@ -677,13 +703,13 @@ static List getEntities(LevelChunk chunk) { return Optional.ofNullable(chunk.level .getEntityLookup() .getChunk(chunk.locX, chunk.locZ)).map(c -> { - try { - //noinspection unchecked - return (List) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(c); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException("Failed to lookup entities [PAPER=true]", e); - } - }).orElse(Collections.emptyList()); + try { + // noinspection unchecked + return (List) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(c); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Failed to lookup entities [PAPER=true]", e); + } + }).orElse(Collections.emptyList()); } try { EntityList entityList = (EntityList) LEVEL_CHUNK_ENTITIES.get(chunk); @@ -694,8 +720,9 @@ static List getEntities(LevelChunk chunk) { } } try { - //noinspection unchecked - return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos()); + // noinspection unchecked + return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))) + .getEntities(chunk.getPos()); } catch (IllegalAccessException e) { collector.add(new RuntimeException("Failed to lookup entities [PAPER=false]", e)); } diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java index cce14ee1ca..ae83557e1f 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java @@ -1,6 +1,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.regen; import com.fastasyncworldedit.bukkit.adapter.Regenerator; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; @@ -43,6 +44,8 @@ import java.nio.file.Path; import java.util.Map; import java.util.OptionalLong; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.function.BooleanSupplier; import java.util.function.Supplier; @@ -54,14 +57,13 @@ public class PaperweightRegen extends Regenerator { private static final Field paperConfigField; private static final Field generatorSettingBaseSupplierField; - static { try { serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField.setAccessible(true); Field tmpPaperConfigField; - try { //only present on paper + try { // only present on paper tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField.setAccessible(true); } catch (Exception e) { @@ -77,7 +79,7 @@ public class PaperweightRegen extends Regenerator { } } - //runtime + // runtime private ServerLevel originalServerWorld; private ServerLevel freshWorld; private LevelStorageSource.LevelStorageAccess session; @@ -88,8 +90,7 @@ public PaperweightRegen( World originalBukkitWorld, Region region, Extent target, - RegenOptions options - ) { + RegenOptions options) { super(originalBukkitWorld, region, target, options); } @@ -111,10 +112,10 @@ protected boolean prepare() { @Override protected boolean initNewWorld() throws Exception { - //world folder + // world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); - //prepare for world init (see upstream implementation for reference) + // prepare for world init (see upstream implementation for reference) org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment(); org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator(); LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir); @@ -134,21 +135,19 @@ protected boolean initNewWorld() throws Exception { originalWorldData.settings.difficulty(), originalWorldData.settings.allowCommands(), originalWorldData.settings.gameRules(), - originalWorldData.settings.getDataConfiguration() - ); + originalWorldData.settings.getDataConfiguration()); - PrimaryLevelData.SpecialWorldProperty specialWorldProperty = - originalWorldData.isFlatWorld() - ? PrimaryLevelData.SpecialWorldProperty.FLAT - : originalWorldData.isDebugWorld() - ? PrimaryLevelData.SpecialWorldProperty.DEBUG - : PrimaryLevelData.SpecialWorldProperty.NONE; - PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable()); + PrimaryLevelData.SpecialWorldProperty specialWorldProperty = originalWorldData.isFlatWorld() + ? PrimaryLevelData.SpecialWorldProperty.FLAT + : originalWorldData.isDebugWorld() + ? PrimaryLevelData.SpecialWorldProperty.DEBUG + : PrimaryLevelData.SpecialWorldProperty.NONE; + PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, + Lifecycle.stable()); BiomeProvider biomeProvider = getBiomeProvider(); - - //init world + // init world freshWorld = Fawe.instance().getQueueHandler().sync((Supplier) () -> new ServerLevel( server, server.executor, @@ -157,8 +156,7 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.dimension(), new LevelStem( originalServerWorld.dimensionTypeRegistration(), - originalServerWorld.getChunkSource().getGenerator() - ), + originalServerWorld.getChunkSource().getGenerator()), new RegenNoOpWorldLoadListener(), originalServerWorld.isDebug(), seed, @@ -167,13 +165,14 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.getRandomSequences(), environment, generator, - biomeProvider - ) { + biomeProvider) { - private final Holder singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess() - .registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow( - WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) - ) : null; + private final Holder singleBiome = options.hasBiomeType() + ? DedicatedServer.getServer().registryAccess() + .registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow( + WorldEditPlugin.getInstance().getBukkitImplAdapter() + .getInternalBiomeId(options.getBiomeType())) + : null; @Override public @NotNull Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { @@ -187,8 +186,7 @@ protected boolean initNewWorld() throws Exception { public void save( @org.jetbrains.annotations.Nullable final ProgressListener progressListener, final boolean flush, - final boolean savingDisabled - ) { + final boolean savingDisabled) { // noop, spigot } @@ -197,20 +195,61 @@ public void save( @Nullable final ProgressListener progressListener, final boolean flush, final boolean savingDisabled, - final boolean close - ) { + final boolean close) { // noop, paper } }).get(); freshWorld.noSave = true; removeWorldFromWorldsMap(); - newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name + newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); // rename to original world name if (paperConfigField != null) { paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); } + + if (FoliaLibHolder.isFolia()) { + return initWorldForFolia(newWorldData); + } + return true; } + private boolean initWorldForFolia(PrimaryLevelData worldData) throws ExecutionException, InterruptedException { + MinecraftServer console = ((CraftServer) Bukkit.getServer()).getServer(); + + ChunkPos spawnChunk = new ChunkPos( + freshWorld.getChunkSource().randomState().sampler().findSpawnPosition()); + + setRandomSpawnSelection(spawnChunk); + + CompletableFuture initFuture = new CompletableFuture<>(); + + org.bukkit.Location spawnLocation = new org.bukkit.Location( + freshWorld.getWorld(), + spawnChunk.x << 4, + 64, + spawnChunk.z << 4); + FoliaLibHolder.getScheduler().runAtLocation(spawnLocation, task -> { + try { + console.initWorld(freshWorld, worldData, worldData, worldData.worldGenOptions()); + initFuture.complete(true); + } catch (Exception e) { + initFuture.completeExceptionally(e); + } + }); + + return initFuture.get(); + } + + private void setRandomSpawnSelection(ChunkPos spawnChunk) { + try { + Field randomSpawnField = ServerLevel.class.getDeclaredField("randomSpawnSelection"); + randomSpawnField.setAccessible(true); + randomSpawnField.set(freshWorld, spawnChunk); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to set randomSpawnSelection for Folia world initialization", e); + } + } + @Override protected void cleanup() { try { @@ -218,7 +257,7 @@ protected void cleanup() { } catch (Exception ignored) { } - //shutdown chunk provider + // shutdown chunk provider try { Fawe.instance().getQueueHandler().sync(() -> { try { @@ -231,29 +270,35 @@ protected void cleanup() { } catch (Exception ignored) { } - //remove world from server + // remove world from server try { Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap); } catch (Exception ignored) { } - //delete directory + // delete directory try { SafeFiles.tryHardToDeleteDir(tempDir); } catch (Exception ignored) { } } + @Override + protected World getFreshWorld() { + return freshWorld != null ? freshWorld.getWorld() : null; + } + @Override protected IChunkCache initSourceQueueCache() { return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } - //util + // util @SuppressWarnings("unchecked") private void removeWorldFromWorldsMap() { try { - Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + Map map = (Map) serverWorldsField + .get(Bukkit.getServer()); map.remove("faweregentempworld"); } catch (IllegalAccessException e) { throw new RuntimeException(e); @@ -280,8 +325,7 @@ public void updateSpawnPos(@NotNull ChunkPos spawnPos) { @Override public void onStatusChange( final @NotNull ChunkPos pos, - @org.jetbrains.annotations.Nullable final ChunkStatus status - ) { + @org.jetbrains.annotations.Nullable final ChunkStatus status) { } diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweAdapter.java index 4e778b2895..a375644aaa 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweAdapter.java @@ -10,6 +10,7 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; @@ -22,6 +23,7 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4.regen.PaperweightRegen; @@ -131,6 +133,7 @@ import java.util.Objects; import java.util.OptionalInt; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -143,12 +146,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter COMPONENTS_CODEC = DataComponentPatch.CODEC.optionalFieldOf( - "components", DataComponentPatch.EMPTY - ).codec(); + "components", DataComponentPatch.EMPTY).codec(); static { try { - CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave"); + CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class + .getDeclaredMethod("wasAccessibleSinceLastSave"); } catch (NoSuchMethodException ignored) { // may not be present in newer paper versions } } @@ -161,8 +164,8 @@ public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodExcepti public Function blockEntityToCompoundTag() { return blockEntity -> FaweCompoundTag.of( - () -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId(DedicatedServer.getServer().registryAccess())) - ); + () -> (LinCompoundTag) toNativeLin( + blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()))); } @Nullable @@ -212,17 +215,16 @@ private synchronized boolean init() { if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { property = new BooleanProperty( state.getName(), - (List) ImmutableList.copyOf(state.getPossibleValues()) - ); + (List) ImmutableList.copyOf(state.getPossibleValues())); } else if (state instanceof DirectionProperty) { property = new DirectionalProperty( state.getName(), state .getPossibleValues() .stream() - .map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase())) - .collect(Collectors.toList()) - ); + .map(e -> Direction + .valueOf(((StringRepresentable) e).getSerializedName().toUpperCase())) + .collect(Collectors.toList())); } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { property = new EnumProperty( state.getName(), @@ -230,13 +232,11 @@ private synchronized boolean init() { .getPossibleValues() .stream() .map(e -> ((StringRepresentable) e).getSerializedName()) - .collect(Collectors.toList()) - ); + .collect(Collectors.toList())); } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { property = new IntegerProperty( state.getName(), - (List) ImmutableList.copyOf(state.getPossibleValues()) - ); + (List) ImmutableList.copyOf(state.getPossibleValues())); } else { throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + state .getClass() @@ -277,7 +277,8 @@ public BlockMaterial getMaterial(BlockType blockType) { @Override public synchronized BlockMaterial getMaterial(BlockState state) { - net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState(); + net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit + .createBlockData(state.getAsString())).getState(); return new PaperweightBlockMaterial(blockState.getBlock(), blockState); } @@ -328,7 +329,8 @@ public BaseBlock getFullBlock(final Location location) { // Read the NBT data BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK); if (blockEntity != null) { - net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()); + net.minecraft.nbt.CompoundTag tag = blockEntity + .saveWithId(DedicatedServer.getServer().registryAccess()); return state.toBaseBlock((LinCompoundTag) toNativeLin(tag)); } } @@ -340,8 +342,7 @@ public BaseBlock getFullBlock(final Location location) { SideEffect.HISTORY, SideEffect.HEIGHTMAPS, SideEffect.LIGHTING, - SideEffect.NEIGHBORS - ); + SideEffect.NEIGHBORS); @Override public Set getSupportedSideEffects() { @@ -361,24 +362,20 @@ public BaseEntity getEntity(org.bukkit.entity.Entity entity) { Entity mcEntity = craftEntity.getHandle(); String id = getEntityId(mcEntity); + EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id); + Supplier saveTag = () -> { + final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag(); + if (!readEntityIntoTag(mcEntity, minecraftTag)) { + return null; + } + // add Id for AbstractChangeSet to work + final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag); + final Map> tags = NbtUtils.getLinCompoundTagValues(tag); + tags.put("Id", LinStringTag.of(id)); + return LinCompoundTag.of(tags); + }; + return new LazyBaseEntity(type, saveTag); - if (id != null) { - EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id); - Supplier saveTag = () -> { - final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag(); - if (!readEntityIntoTag(mcEntity, minecraftTag)) { - return null; - } - //add Id for AbstractChangeSet to work - final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag); - final Map> tags = NbtUtils.getLinCompoundTagValues(tag); - tags.put("Id", LinStringTag.of(id)); - return LinCompoundTag.of(tags); - }; - return new LazyBaseEntity(type, saveTag); - } else { - return null; - } } @Override @@ -428,8 +425,7 @@ public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockSt return (char) ibdToOrdinal[id]; } catch (ArrayIndexOutOfBoundsException e1) { LOGGER.error("Attempted to convert {} with ID {} to char. ibdToOrdinal length: {}. Defaulting to air!", - blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1 - ); + blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1); return BlockTypesCache.ReservedIDs.AIR; } } @@ -502,18 +498,22 @@ public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockSt @Override public void sendFakeChunk(World world, Player player, ChunkPacket chunkPacket) { ServerLevel nmsWorld = getServerLevel(world); - ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); + ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), + chunkPacket.getChunkZ()); if (map != null && wasAccessibleSinceLastSave(map)) { boolean flag = false; // PlayerChunk.d players = map.players; - Stream stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag) - */ Stream.empty(); + Stream stream = /* + * players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), + * flag) + */ Stream.empty(); ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle(); stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer) .forEach(entityPlayer -> { synchronized (chunkPacket) { - ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket.getNativePacket(); + ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket + .getNativePacket(); if (nmsPacket == null) { nmsPacket = mapUtil.create(this, chunkPacket); chunkPacket.setNativePacket(nmsPacket); @@ -540,18 +540,18 @@ public boolean canPlaceAt(World world, BlockVector3 blockVector3, BlockState blo net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); return blockState1.hasPostProcess( getServerLevel(world), - new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z()) - ); + new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z())); } @Override public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) { final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess(); ItemStack stack = new ItemStack( - registryAccess.registryOrThrow(Registries.ITEM).get(ResourceLocation.tryParse(baseItemStack.getType().id())), - baseItemStack.getAmount() - ); - final net.minecraft.nbt.CompoundTag nbt = (net.minecraft.nbt.CompoundTag) fromNative(baseItemStack.getNbtData()); + registryAccess.registryOrThrow(Registries.ITEM) + .get(ResourceLocation.tryParse(baseItemStack.getType().id())), + baseItemStack.getAmount()); + final net.minecraft.nbt.CompoundTag nbt = (net.minecraft.nbt.CompoundTag) fromNative( + baseItemStack.getNbtData()); if (nbt != null) { final DataComponentPatch patch = COMPONENTS_CODEC .parse(registryAccess.createSerializationContext(NbtOps.INSTANCE), nbt) @@ -584,9 +584,22 @@ protected ServerLevel getServerLevel(final World world) { return ((CraftWorld) world).getHandle(); } + private T syncRegion(World world, BlockVector3 pt, java.util.function.Supplier supplier) { + if (FoliaLibHolder.isFolia()) { + Location location = new Location(world, pt.x(), pt.y(), pt.z()); + CompletableFuture future = new CompletableFuture<>(); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> future.complete(supplier.get())); + return future.join(); + } + return TaskManager.taskManager().sync(supplier); + } + @Override - public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, BlockVector3 pt) { - //FAWE start + public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, + BlockVector3 pt) { + // FAWE start ServerLevel serverLevel = getServerLevel(world); ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); @@ -596,15 +609,14 @@ public boolean generateFeature(ConfiguredFeatureType feature, World world, EditS .get(ResourceLocation.tryParse(feature.id())); FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { if (!configuredFeature.place( populator, generator, serverLevel.random, - new BlockPos(pt.x(), pt.y(), pt.z()) - )) { + new BlockPos(pt.x(), pt.y(), pt.z()))) { return null; } List placedBlocks = new ArrayList<>(populator.getList()); @@ -616,7 +628,7 @@ public boolean generateFeature(ConfiguredFeatureType feature, World world, EditS }); return placeFeatureIntoSession(editSession, populator, placed); - //FAWE end + // FAWE end } @Override @@ -634,9 +646,9 @@ public boolean generateStructure(StructureType type, World world, EditSession ed ChunkPos chunkPos = new ChunkPos(new BlockPos(pt.x(), pt.y(), pt.z())); - //FAWE start + // FAWE start FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { StructureStart structureStart = k.generate( @@ -649,20 +661,17 @@ public boolean generateStructure(StructureType type, World world, EditSession ed chunkPos, 0, populator, - biome -> true - ); + biome -> true); if (!structureStart.isValid()) { return null; } else { BoundingBox boundingBox = structureStart.getBoundingBox(); ChunkPos min = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.minX()), - SectionPos.blockToSectionCoord(boundingBox.minZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.minZ())); ChunkPos max = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.maxX()), - SectionPos.blockToSectionCoord(boundingBox.maxZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.maxZ())); ChunkPos.rangeClosed(min, max).forEach((chunkPosx) -> structureStart.placeInChunk( populator, serverLevel.structureManager(), @@ -674,10 +683,8 @@ public boolean generateStructure(StructureType type, World world, EditSession ed chunkPosx.getMinBlockZ(), chunkPosx.getMaxBlockX(), serverLevel.getMaxBuildHeight(), - chunkPosx.getMaxBlockZ() - ), - chunkPosx - )); + chunkPosx.getMaxBlockZ()), + chunkPosx)); List placedBlocks = new ArrayList<>(populator.getList()); placedBlocks.addAll(serverLevel.capturedBlockStates.values()); return placedBlocks; @@ -688,14 +695,13 @@ public boolean generateStructure(StructureType type, World world, EditSession ed }); return placeFeatureIntoSession(editSession, populator, placed); - //FAWE end + // FAWE end } private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, - final List placed - ) { + final List placed) { if (placed == null || placed.isEmpty()) { return false; } @@ -705,10 +711,12 @@ private boolean placeFeatureIntoSession( continue; } BlockPos pos = craftBlockState.getPosition(); - editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), BukkitAdapter.adapt(craftBlockState.getBlockData())); + editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), + BukkitAdapter.adapt(craftBlockState.getBlockData())); BlockEntity blockEntity = populator.getBlockEntity(pos); if (blockEntity != null) { - net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()); + net.minecraft.nbt.CompoundTag tag = blockEntity + .saveWithId(DedicatedServer.getServer().registryAccess()); editSession.setTile(pos.getX(), pos.getY(), pos.getZ(), (CompoundTag) toNative(tag)); } } @@ -721,7 +729,8 @@ public void setupFeatures() { // All these features should be the "face" selected Set face_features = Arrays - .stream(new Class[]{AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, VegetationFeatures.class}) + .stream(new Class[] { AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, + VegetationFeatures.class }) .flatMap(c -> Arrays.stream(c.getFields())) .filter(f -> { int modifiers = f.getModifiers(); @@ -774,16 +783,15 @@ public void setupFeatures() { public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess(); final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); - // We should be fine to perform this later as we're using a deep-copied itemstack (above) + // We should be fine to perform this later as we're using a deep-copied + // itemstack (above) final Supplier tag = () -> COMPONENTS_CODEC.encodeStart( registryAccess.createSerializationContext(NbtOps.INSTANCE), - nmsStack.getComponentsPatch() - ).getOrThrow(); + nmsStack.getComponentsPatch()).getOrThrow(); return new BaseItemStack( BukkitAdapter.asItemType(itemStack.getType()), LazyReference.from(() -> (LinCompoundTag) toNativeLin(tag.get())), - itemStack.getAmount() - ); + itemStack.getAmount()); } @Override diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweWorldNativeAccess.java index 4fa9988b81..68d18726f6 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweWorldNativeAccess.java @@ -21,6 +21,7 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.chunk.LevelChunk; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.event.block.BlockPhysicsEvent; @@ -59,7 +60,7 @@ public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAd this.level = level; // Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging. // - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway. - this.lastTick = new AtomicInteger(MinecraftServer.currentTick); + this.lastTick = new AtomicInteger(getCurrentTick()); } private Level getLevel() { @@ -95,7 +96,7 @@ public synchronized net.minecraft.world.level.block.state.BlockState setBlockSta LevelChunk levelChunk, BlockPos blockPos, net.minecraft.world.level.block.state.BlockState blockState ) { - int currentTick = MinecraftServer.currentTick; + int currentTick = getCurrentTick(); if (Fawe.isMainThread()) { return levelChunk.setBlockState(blockPos, blockState, this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE) @@ -290,4 +291,16 @@ private record CachedChange( } + private int getCurrentTick() { + try { + return MinecraftServer.currentTick; + } catch (NoSuchFieldError e) { + try { + return Bukkit.getCurrentTick(); + } catch (Exception ex) { + return 0; + } + } + } + } diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java index 6195054ffe..430dafd686 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java @@ -11,6 +11,7 @@ import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; @@ -57,6 +58,8 @@ import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.lighting.LevelLightEngine; import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.World; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.CraftBlock; @@ -94,7 +97,8 @@ public class PaperweightGetBlocks extends AbstractBukkitGetBlocks posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); + private static final Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), + v.getZ()); public static final Function NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin .getInstance() .getBukkitImplAdapter()).blockEntityToCompoundTag(); @@ -167,8 +171,9 @@ public BiomeType getBiomeType(int x, int y, int z) { @Override public void removeSectionLighting(int layer, boolean sky) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK) + .getDataLayerData( + sectionPos); if (dataLayer != null) { lightUpdate = true; synchronized (dataLayer) { @@ -195,9 +200,8 @@ public void removeSectionLighting(int layer, boolean sky) { @Override 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))); + BlockEntity blockEntity = getChunk() + .getBlockEntity(new BlockPos((x & 15) + (chunkX << 4), y, (z & 15) + (chunkZ << 4))); if (blockEntity == null) { return null; } @@ -220,19 +224,19 @@ public int getSkyLight(int x, int y, int z) { int alayer = layer - getMinSectionPosition(); if (skyLight[alayer] == null) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = - serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getDataLayerData(sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY) + .getDataLayerData(sectionPos); // If the server hasn't generated the section's NibbleArray yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( LightLayer.BLOCK, sectionPos, - dataLayer - ); + dataLayer); } skyLight[alayer] = dataLayer; } @@ -254,12 +258,13 @@ public int getEmittedLight(int x, int y, int z) { // If the server hasn't generated the section's DataLayer yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); - ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, sectionPos, - dataLayer - ); + ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, + sectionPos, + dataLayer); } blockLight[alayer] = dataLayer; } @@ -332,8 +337,7 @@ protected > T internalCall( Runnable finalizer, int copyKey, LevelChunk nmsChunk, - ServerLevel nmsWorld - ) throws Exception { + ServerLevel nmsWorld) throws Exception { PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null; if (createCopy) { if (copies.containsKey(copyKey)) { @@ -363,7 +367,18 @@ protected > T internalCall( beacons = new ArrayList<>(); } beacons.add(tile); - PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + if (FoliaLibHolder.isFolia()) { + Location location = new Location( + nmsWorld.getWorld(), + tile.getBlockPos().getX(), + tile.getBlockPos().getY(), + tile.getBlockPos().getZ()); + FoliaLibHolder.getScheduler().runAtLocation( + location, + (task) -> PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk)); + } else { + PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + } continue; } nmsChunk.removeBlockEntity(tile.getBlockPos()); @@ -400,26 +415,25 @@ protected > T internalCall( } if (existingSection == null) { - PalettedContainer> biomeData = PaperweightPlatformAdapter.getBiomePalettedContainer( - biomes[setSectionIndex], - biomeHolderIdMap - ); + PalettedContainer> biomeData = PaperweightPlatformAdapter + .getBiomePalettedContainer( + biomes[setSectionIndex], + biomeHolderIdMap); LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, new char[4096], adapter, biomeRegistry, - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { - updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex); + getSectionIndex)) { + updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], + getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; @@ -428,8 +442,7 @@ protected > T internalCall( "Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); continue; } } @@ -437,8 +450,7 @@ protected > T internalCall( PalettedContainer> paletteBiomes = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); if (paletteBiomes != null) { PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes); } @@ -450,13 +462,16 @@ protected > T internalCall( bitMask |= 1 << getSectionIndex; - // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to - // this chunk GET when #updateGet is called. Future dords, please listen this time. + // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in + // order to write changes to + // this chunk GET when #updateGet is called. Future dords, please listen this + // time. char[] tmp = set.load(layerNo); char[] setArr = new char[tmp.length]; System.arraycopy(tmp, 0, setArr, 0, tmp.length); - // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was + // synchronise on internal section to avoid circular locking with a continuing + // edit if the chunk was // submitted to keep loaded internal chunks to queue target size. synchronized (super.sectionLocks[getSectionIndex]) { @@ -484,23 +499,22 @@ protected > T internalCall( PalettedContainer> biomeData = biomes == null ? new PalettedContainer<>( biomeHolderIdMap, biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap); + PalettedContainer.Strategy.SECTION_BIOMES) + : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], + biomeHolderIdMap); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, setArr, adapter, biomeRegistry, - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { + getSectionIndex)) { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); continue; } else { @@ -510,14 +524,14 @@ protected > T internalCall( "Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); continue; } } } - //ensure that the server doesn't try to tick the chunksection while we're editing it. (Again) + // ensure that the server doesn't try to tick the chunksection while we're + // editing it. (Again) PaperweightPlatformAdapter.clearCounts(existingSection); if (PaperLib.isPaper()) { existingSection.tickingList.clear(); @@ -538,8 +552,10 @@ protected > T internalCall( this.reset(); } else if (!Arrays.equals(update(getSectionIndex, new char[4096], true), load(layerNo))) { this.reset(layerNo); - /*} else if (lock.isModified()) { - this.reset(layerNo);*/ + /* + * } else if (lock.isModified()) { + * this.reset(layerNo); + */ } } finally { sectionLock.writeLock().unlock(); @@ -548,8 +564,7 @@ protected > T internalCall( PalettedContainer> biomeData = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, @@ -557,22 +572,20 @@ protected > T internalCall( setArr, adapter, biomeRegistry, - biomeData != null ? biomeData : (PalettedContainer>) existingSection.getBiomes() - ); + biomeData != null ? biomeData + : (PalettedContainer>) existingSection.getBiomes()); if (!PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, existingSection, newSection, - getSectionIndex - )) { + getSectionIndex)) { LOGGER.error( "Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); } else { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); } @@ -584,12 +597,12 @@ protected > T internalCall( for (Map.Entry entry : heightMaps.entrySet()) { PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue()); } - PaperweightGetBlocks.this.setLightingToGet(set.getLight(), set.getMinSectionPosition(), set.getMaxSectionPosition()); + PaperweightGetBlocks.this.setLightingToGet(set.getLight(), set.getMinSectionPosition(), + set.getMaxSectionPosition()); PaperweightGetBlocks.this.setSkyLightingToGet( set.getSkyLight(), set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); + set.getMaxSectionPosition()); List syncTasks = new ArrayList<>(); @@ -602,7 +615,8 @@ protected > T internalCall( final List finalBeacons = beacons; syncTasks.add(() -> { for (BlockEntity beacon : finalBeacons) { - BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), SoundEvents.BEACON_DEACTIVATE); + BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), + SoundEvents.BEACON_DEACTIVATE); new BeaconDeactivatedEvent(CraftBlock.at(beacon.getLevel(), beacon.getBlockPos())).callEvent(); } }); @@ -664,25 +678,42 @@ protected > T internalCall( if (type != null) { Entity entity = type.create(nmsWorld); if (entity != null) { - final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin( - linTag); + 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(NbtUtils.uuid(nativeTag)); - if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { - LOGGER.warn( - "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", - id, - nmsWorld.getWorld().getName(), - x, - y, - z - ); - // Unsuccessful create should not be saved to history - iterator.remove(); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, (task) -> { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } + }); + } else { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } } } } @@ -702,18 +733,39 @@ protected > T internalCall( final int z = blockHash.z() + bz; final BlockPos pos = new BlockPos(x, y, z); - synchronized (nmsWorld) { - BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); - if (tileEntity == null || tileEntity.isRemoved()) { - nmsWorld.removeBlockEntity(pos); - tileEntity = nmsWorld.getBlockEntity(pos); - } - if (tileEntity != null) { - 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)); - tileEntity.loadWithComponents(tag, DedicatedServer.getServer().registryAccess()); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, (task) -> { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + tileEntity.loadWithComponents(tag, + DedicatedServer.getServer().registryAccess()); + } + } + }); + } else { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + tileEntity.loadWithComponents(tag, DedicatedServer.getServer().registryAccess()); + } } } } @@ -735,7 +787,8 @@ protected > T internalCall( // send to player if (!set .getSideEffectSet() - .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) { + .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING + || finalMask == 0 && biomes != null) { this.send(); } if (finalizer != null) { @@ -752,8 +805,7 @@ private void updateGet( LevelChunkSection[] chunkSections, LevelChunkSection section, char[] arr, - int layer - ) { + int layer) { try { sectionLock.writeLock().lock(); if (this.getChunk() != nmsChunk) { @@ -767,8 +819,9 @@ private void updateGet( System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); } if (this.sections[layer] != section) { - // Not sure why it's funky, but it's what I did in commit fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords - this.sections[layer] = new LevelChunkSection[]{section}.clone()[0]; + // Not sure why it's funky, but it's what I did in commit + // fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords + this.sections[layer] = new LevelChunkSection[] { section }.clone()[0]; } } finally { sectionLock.writeLock().unlock(); @@ -784,14 +837,19 @@ public void send() { } /** - * Update a given (nullable) data array to the current data stored in the server's chunk, associated with this - * {@link PaperweightPlatformAdapter} instance. Not synchronised to the {@link PaperweightPlatformAdapter} instance as synchronisation - * is handled where necessary in the method, and should otherwise be handled correctly by this method's caller. + * Update a given (nullable) data array to the current data stored in the + * server's chunk, associated with this + * {@link PaperweightPlatformAdapter} instance. Not synchronised to the + * {@link PaperweightPlatformAdapter} instance as synchronisation + * is handled where necessary in the method, and should otherwise be handled + * correctly by this method's caller. * - * @param layer layer index (0 may denote a negative layer in the world, e.g. at y=-32) + * @param layer layer index (0 may denote a negative layer in the world, + * e.g. at y=-32) * @param data array to be updated/filled with data or null * @param aggressive if the cached section array should be re-acquired. - * @return the given array to be filled with data, or a new array if null is given. + * @return the given array to be filled with data, or a new array if null is + * given. */ @Override @SuppressWarnings("unchecked") @@ -821,7 +879,8 @@ public char[] update(int layer, char[] data, boolean aggressive) { return data; } - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get(dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get(dataObject); final int bitsPerEntry = bits.getBits(); final long[] blockStates = bits.getRaw(); @@ -901,10 +960,10 @@ public LevelChunk getChunk() { } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e); throw new FaweException( - TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), + TextComponent + .of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), FaweException.Type.OTHER, - false - ); + false); } } } @@ -912,14 +971,16 @@ public LevelChunk getChunk() { return levelChunk; } - private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, int maxSectionPosition) { + private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, + int maxSectionPosition) { for (int Y = 0; Y <= maxSectionPosition - minSectionPosition; Y++) { if (light[Y] == null) { continue; } SectionPos sectionPos = SectionPos.of(levelChunk.getPos(), Y + minSectionPosition); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer) + .getDataLayerData( + sectionPos); if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; Arrays.fill(LAYER_COUNT, lightLayer == LightLayer.SKY ? (byte) 15 : (byte) 0); @@ -927,8 +988,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( lightLayer, sectionPos, - dataLayer - ); + dataLayer); } synchronized (dataLayer) { for (int x = 0; x < 16; x++) { @@ -948,8 +1008,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti private PalettedContainer> setBiomesToPalettedContainer( final BiomeType[][] biomes, final int sectionIndex, - final PalettedContainerRO> data - ) { + final PalettedContainerRO> data) { BiomeType[] sectionBiomes; if (biomes == null || (sectionBiomes = biomes[sectionIndex]) == null) { return null; @@ -966,8 +1025,7 @@ private PalettedContainer> setBiomesToPalettedContainer( x, y, z, - biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType)) - ); + biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType))); } } } @@ -1023,8 +1081,9 @@ public boolean trim(boolean aggressive) { final PalettedContainer blocksExisting = existing.getStates(); final Object dataObject = PaperweightPlatformAdapter.fieldData.get(blocksExisting); - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get( - dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get( + dataObject); int paletteSize; if (palette instanceof LinearPalette || palette instanceof HashMapPalette) { @@ -1034,7 +1093,8 @@ public boolean trim(boolean aggressive) { continue; } if (paletteSize == 1) { - //If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks. + // If the cached palette size is 1 then no blocks can have been changed i.e. do + // not need to update these chunks. continue; } super.trim(false, i); diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPlatformAdapter.java index b6da05cc6e..df4046c308 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPlatformAdapter.java @@ -8,6 +8,7 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.bukkit.WorldEditPlugin; @@ -19,6 +20,7 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import io.papermc.lib.PaperLib; +import io.papermc.paper.util.MCUtil; import io.papermc.paper.world.ChunkEntitySlices; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; @@ -129,13 +131,16 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c")); fieldPalette.setAccessible(true); - fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "g")); + fieldTickingFluidCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingFluidCount", "g")); fieldTickingFluidCount.setAccessible(true); - fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); + fieldTickingBlockCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); fieldTickingBlockCount.setAccessible(true); Field tmpFieldBiomes; try { - // Seems it's sometimes biomes and sometimes "i". Idk this is just easier than having to try to deal with it + // Seems it's sometimes biomes and sometimes "i". Idk this is just easier than + // having to try to deal with it tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("biomes"); // apparently unobf } catch (NoSuchFieldException ignored) { tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("i"); // apparently obf @@ -145,13 +150,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName( "getVisibleChunkIfPresent", - "b" - ), long.class); + "b"), long.class); getVisibleChunkIfPresent.setAccessible(true); methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent); if (!PaperLib.isPaper()) { - fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f")); + fieldThreadingDetector = PalettedContainer.class + .getDeclaredField(Refraction.pickName("threadingDetector", "f")); fieldThreadingDetector.setAccessible(true); fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); fieldLock.setAccessible(true); @@ -164,17 +169,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( Refraction.pickName("removeGameEventListener", "a"), BlockEntity.class, - ServerLevel.class - ); + ServerLevel.class); removeGameEventListener.setAccessible(true); methodRemoveGameEventListener = lookup.unreflect(removeGameEventListener); Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod( Refraction.pickName( "removeBlockEntityTicker", - "k" - ), BlockPos.class - ); + "k"), + BlockPos.class); removeBlockEntityTicker.setAccessible(true); methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker); @@ -198,7 +201,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } try { // Non-Paper - SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "N")); + SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class + .getDeclaredField(Refraction.pickName("entityManager", "N")); SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true); } catch (NoSuchFieldException ignored) { } @@ -206,8 +210,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method palettedContaienrGet = PalettedContainer.class.getDeclaredMethod( Refraction.pickName("get", "a"), - int.class - ); + int.class); palettedContaienrGet.setAccessible(true); PALETTED_CONTAINER_GET = lookup.unreflect(palettedContaienrGet); } catch (RuntimeException | Error e) { @@ -223,14 +226,13 @@ static boolean setSectionAtomic( LevelChunkSection[] sections, LevelChunkSection expected, LevelChunkSection value, - int layer - ) { + int layer) { return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer); } // There is no point in having a functional semaphore for paper servers. - private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = - ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null)); + private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = ThreadLocal + .withInitial(() -> new DelegateSemaphore(1, null)); static DelegateSemaphore applyLock(LevelChunkSection section) { if (PaperLib.isPaper()) { @@ -285,11 +287,11 @@ public static CompletableFuture ensureLoaded(ServerLevel serverLevel "Unexpected error when getting completed future at chunk {},{}. Returning to default.", chunkX, chunkZ, - e - ); + e); } } - return CompletableFuture.supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); + return CompletableFuture + .supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); } private static LevelChunk toLevelChunk(Chunk chunk) { @@ -326,6 +328,14 @@ private static LevelChunk toLevelChunk(Chunk chunk) { } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { + if (FoliaLibHolder.isFolia()) { + try { + serverLevel.getChunkSource().addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), + 0, Unit.INSTANCE); + } catch (Exception ignored) { + } + return; + } // Ensure chunk is definitely loaded before applying a ticket io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel .getChunkSource() @@ -363,7 +373,7 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int if (lockHolder.chunkLock == null) { return; } - MinecraftServer.getServer().execute(() -> { + if (FoliaLibHolder.isFolia()) { try { ClientboundLevelChunkWithLightPacket packet; if (PaperLib.isPaper()) { @@ -380,14 +390,39 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int levelChunk, nmsWorld.getChunkSource().getLightEngine(), null, - null - ); + null); } nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet)); } finally { NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); } - }); + } else { + MinecraftServer.getServer().execute(() -> { + try { + ChunkPos pos = levelChunk.getPos(); + ClientboundLevelChunkWithLightPacket packet; + if (PaperLib.isPaper()) { + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null, + false // last false is to not bother with x-ray + ); + } else { + // deprecated on paper - deprecation suppressed + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null); + } + nearbyPlayers(nmsWorld, pos).forEach(p -> p.connection.send(packet)); + } finally { + NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); + } + }); + } } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { @@ -395,15 +430,14 @@ private static List nearbyPlayers(ServerLevel serverLevel, ChunkPo } /* - NMS conversion + * NMS conversion */ public static LevelChunkSection newChunkSection( final int layer, final char[] blocks, CachedBukkitAdapter adapter, Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { return newChunkSection(layer, null, blocks, adapter, biomeRegistry, biomes); } @@ -413,8 +447,7 @@ public static LevelChunkSection newChunkSection( char[] set, CachedBukkitAdapter adapter, Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (set == null) { return newChunkSection(biomeRegistry, biomes); } @@ -470,15 +503,15 @@ public static LevelChunkSection newChunkSection( } // Create palette with data - @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot - final PalettedContainer blockStatePalettedContainer = - new PalettedContainer<>( - Block.BLOCK_STATE_REGISTRY, - PalettedContainer.Strategy.SECTION_STATES, - PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, bitsPerEntry), - nmsBits, - palette - ); + @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility + // with spigot + final PalettedContainer blockStatePalettedContainer = new PalettedContainer<>( + Block.BLOCK_STATE_REGISTRY, + PalettedContainer.Strategy.SECTION_STATES, + PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, + bitsPerEntry), + nmsBits, + palette); if (biomes == null) { IdMap> biomeHolderIdMap = biomeRegistry.asHolderIdMap(); biomes = new PalettedContainer<>( @@ -488,8 +521,7 @@ public static LevelChunkSection newChunkSection( .getBukkitImplAdapter() .getInternalBiomeId( BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ); + PalettedContainer.Strategy.SECTION_BIOMES); } return new LevelChunkSection(blockStatePalettedContainer, biomes); @@ -506,16 +538,14 @@ public static LevelChunkSection newChunkSection( @SuppressWarnings("deprecation") // Only deprecated in paper private static LevelChunkSection newChunkSection( Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (biomes == null) { return new LevelChunkSection(biomeRegistry); } PalettedContainer dataPaletteBlocks = new PalettedContainer<>( Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), - PalettedContainer.Strategy.SECTION_STATES - ); + PalettedContainer.Strategy.SECTION_STATES); return new LevelChunkSection(dataPaletteBlocks, biomes); } @@ -528,17 +558,18 @@ public static void setBiomesToChunkSection(LevelChunkSection section, PalettedCo } /** - * Create a new {@link PalettedContainer}. Should only be used if no biome container existed beforehand. + * Create a new {@link PalettedContainer}. Should only be used if no + * biome container existed beforehand. */ public static PalettedContainer> getBiomePalettedContainer( BiomeType[] biomes, - IdMap> biomeRegistry - ) { + IdMap> biomeRegistry) { if (biomes == null) { return null; } BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - // Don't stream this as typically will see 1-4 biomes; stream overhead is large for the small length + // Don't stream this as typically will see 1-4 biomes; stream overhead is large + // for the small length Map> palette = new HashMap<>(); for (BiomeType biomeType : new LinkedList<>(Arrays.asList(biomes))) { Holder biome; @@ -553,16 +584,14 @@ public static PalettedContainer> getBiomePalettedContainer( int bitsPerEntry = MathMan.log2nlz(biomeCount - 1); Object configuration = PalettedContainer.Strategy.SECTION_STATES.getConfiguration( new FakeIdMapBiome(biomeCount), - bitsPerEntry - ); + bitsPerEntry); if (bitsPerEntry > 3) { bitsPerEntry = MathMan.log2nlz(biomeRegistry.size() - 1); } PalettedContainer> biomePalettedContainer = new PalettedContainer<>( biomeRegistry, biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ); + PalettedContainer.Strategy.SECTION_BIOMES); final Palette> biomePalette; if (bitsPerEntry == 0) { @@ -597,12 +626,11 @@ public static PalettedContainer> getBiomePalettedContainer( int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes final int arrayLength = MathMan.longArrayLength(bitsPerEntryNonZero, 64); - - BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) : new SimpleBitStorage( - bitsPerEntry, - 64, - new long[arrayLength] - ); + BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) + : new SimpleBitStorage( + bitsPerEntry, + 64, + new long[arrayLength]); try { Object data = dataConstructor.newInstance(configuration, bitStorage, biomePalette); @@ -671,13 +699,13 @@ static List getEntities(LevelChunk chunk) { return Optional.ofNullable(chunk.level .getEntityLookup() .getChunk(chunk.locX, chunk.locZ)).map(c -> { - try { - //noinspection unchecked - return (List) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(c); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException("Failed to lookup entities [PAPER=true]", e); - } - }).orElse(Collections.emptyList()); + try { + // noinspection unchecked + return (List) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(c); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Failed to lookup entities [PAPER=true]", e); + } + }).orElse(Collections.emptyList()); } try { EntityList entityList = (EntityList) LEVEL_CHUNK_ENTITIES.get(chunk); @@ -688,8 +716,9 @@ static List getEntities(LevelChunk chunk) { } } try { - //noinspection unchecked - return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos()); + // noinspection unchecked + return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))) + .getEntities(chunk.getPos()); } catch (IllegalAccessException e) { collector.add(new RuntimeException("Failed to lookup entities [PAPER=false]", e)); } diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/regen/PaperweightRegen.java index d80d098ffd..6cc66c10bb 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/regen/PaperweightRegen.java @@ -1,6 +1,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4.regen; import com.fastasyncworldedit.bukkit.adapter.Regenerator; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; @@ -42,6 +43,8 @@ import java.nio.file.Path; import java.util.Map; import java.util.OptionalLong; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.function.BooleanSupplier; import java.util.function.Supplier; @@ -53,14 +56,13 @@ public class PaperweightRegen extends Regenerator { private static final Field paperConfigField; private static final Field generatorSettingBaseSupplierField; - static { try { serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField.setAccessible(true); Field tmpPaperConfigField; - try { //only present on paper + try { // only present on paper tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField.setAccessible(true); } catch (Exception e) { @@ -76,7 +78,7 @@ public class PaperweightRegen extends Regenerator { } } - //runtime + // runtime private ServerLevel originalServerWorld; private ServerLevel freshWorld; private LevelStorageSource.LevelStorageAccess session; @@ -87,8 +89,7 @@ public PaperweightRegen( World originalBukkitWorld, Region region, Extent target, - RegenOptions options - ) { + RegenOptions options) { super(originalBukkitWorld, region, target, options); } @@ -110,10 +111,10 @@ protected boolean prepare() { @Override protected boolean initNewWorld() throws Exception { - //world folder + // world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); - //prepare for world init (see upstream implementation for reference) + // prepare for world init (see upstream implementation for reference) org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment(); org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator(); LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir); @@ -133,21 +134,19 @@ protected boolean initNewWorld() throws Exception { originalWorldData.settings.difficulty(), originalWorldData.settings.allowCommands(), originalWorldData.settings.gameRules(), - originalWorldData.settings.getDataConfiguration() - ); + originalWorldData.settings.getDataConfiguration()); - PrimaryLevelData.SpecialWorldProperty specialWorldProperty = - originalWorldData.isFlatWorld() - ? PrimaryLevelData.SpecialWorldProperty.FLAT - : originalWorldData.isDebugWorld() - ? PrimaryLevelData.SpecialWorldProperty.DEBUG - : PrimaryLevelData.SpecialWorldProperty.NONE; - PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable()); + PrimaryLevelData.SpecialWorldProperty specialWorldProperty = originalWorldData.isFlatWorld() + ? PrimaryLevelData.SpecialWorldProperty.FLAT + : originalWorldData.isDebugWorld() + ? PrimaryLevelData.SpecialWorldProperty.DEBUG + : PrimaryLevelData.SpecialWorldProperty.NONE; + PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, + Lifecycle.stable()); BiomeProvider biomeProvider = getBiomeProvider(); - - //init world + // init world freshWorld = Fawe.instance().getQueueHandler().sync((Supplier) () -> new ServerLevel( server, server.executor, @@ -156,8 +155,7 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.dimension(), new LevelStem( originalServerWorld.dimensionTypeRegistration(), - originalServerWorld.getChunkSource().getGenerator() - ), + originalServerWorld.getChunkSource().getGenerator()), new RegenNoOpWorldLoadListener(), originalServerWorld.isDebug(), seed, @@ -166,13 +164,14 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.getRandomSequences(), environment, generator, - biomeProvider - ) { + biomeProvider) { - private final Holder singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess() - .registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow( - WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) - ) : null; + private final Holder singleBiome = options.hasBiomeType() + ? DedicatedServer.getServer().registryAccess() + .registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow( + WorldEditPlugin.getInstance().getBukkitImplAdapter() + .getInternalBiomeId(options.getBiomeType())) + : null; @Override public @NotNull Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { @@ -186,8 +185,7 @@ protected boolean initNewWorld() throws Exception { public void save( @org.jetbrains.annotations.Nullable final ProgressListener progressListener, final boolean flush, - final boolean savingDisabled - ) { + final boolean savingDisabled) { // noop, spigot } @@ -196,20 +194,61 @@ public void save( @Nullable final ProgressListener progressListener, final boolean flush, final boolean savingDisabled, - final boolean close - ) { + final boolean close) { // noop, paper } }).get(); freshWorld.noSave = true; removeWorldFromWorldsMap(); - newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name + newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); // rename to original world name if (paperConfigField != null) { paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); } + + if (FoliaLibHolder.isFolia()) { + return initWorldForFolia(newWorldData); + } + return true; } + private boolean initWorldForFolia(PrimaryLevelData worldData) throws ExecutionException, InterruptedException { + MinecraftServer console = ((CraftServer) Bukkit.getServer()).getServer(); + + ChunkPos spawnChunk = new ChunkPos( + freshWorld.getChunkSource().randomState().sampler().findSpawnPosition()); + + setRandomSpawnSelection(spawnChunk); + + CompletableFuture initFuture = new CompletableFuture<>(); + + org.bukkit.Location spawnLocation = new org.bukkit.Location( + freshWorld.getWorld(), + spawnChunk.x << 4, + 64, + spawnChunk.z << 4); + FoliaLibHolder.getScheduler().runAtLocation(spawnLocation, task -> { + try { + console.initWorld(freshWorld, worldData, worldData, worldData.worldGenOptions()); + initFuture.complete(true); + } catch (Exception e) { + initFuture.completeExceptionally(e); + } + }); + + return initFuture.get(); + } + + private void setRandomSpawnSelection(ChunkPos spawnChunk) { + try { + Field randomSpawnField = ServerLevel.class.getDeclaredField("randomSpawnSelection"); + randomSpawnField.setAccessible(true); + randomSpawnField.set(freshWorld, spawnChunk); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to set randomSpawnSelection for Folia world initialization", e); + } + } + @Override protected void cleanup() { try { @@ -217,7 +256,7 @@ protected void cleanup() { } catch (Exception ignored) { } - //shutdown chunk provider + // shutdown chunk provider try { Fawe.instance().getQueueHandler().sync(() -> { try { @@ -230,29 +269,35 @@ protected void cleanup() { } catch (Exception ignored) { } - //remove world from server + // remove world from server try { Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap); } catch (Exception ignored) { } - //delete directory + // delete directory try { SafeFiles.tryHardToDeleteDir(tempDir); } catch (Exception ignored) { } } + @Override + protected World getFreshWorld() { + return freshWorld != null ? freshWorld.getWorld() : null; + } + @Override protected IChunkCache initSourceQueueCache() { return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } - //util + // util @SuppressWarnings("unchecked") private void removeWorldFromWorldsMap() { try { - Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + Map map = (Map) serverWorldsField + .get(Bukkit.getServer()); map.remove("faweregentempworld"); } catch (IllegalAccessException e) { throw new RuntimeException(e); @@ -279,8 +324,7 @@ public void updateSpawnPos(@NotNull ChunkPos spawnPos) { @Override public void onStatusChange( final @NotNull ChunkPos pos, - @org.jetbrains.annotations.Nullable final net.minecraft.world.level.chunk.status.ChunkStatus status - ) { + @org.jetbrains.annotations.Nullable final net.minecraft.world.level.chunk.status.ChunkStatus status) { } diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java index 7be8d693be..bf10774558 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java @@ -10,6 +10,7 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; @@ -22,6 +23,7 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.regen.PaperweightRegen; @@ -131,6 +133,7 @@ import java.util.Objects; import java.util.OptionalInt; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -143,12 +146,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter COMPONENTS_CODEC = DataComponentPatch.CODEC.optionalFieldOf( - "components", DataComponentPatch.EMPTY - ).codec(); + "components", DataComponentPatch.EMPTY).codec(); static { try { - CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave"); + CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class + .getDeclaredMethod("wasAccessibleSinceLastSave"); } catch (NoSuchMethodException ignored) { // may not be present in newer paper versions } } @@ -161,8 +164,8 @@ public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodExcepti public Function blockEntityToCompoundTag() { return blockEntity -> FaweCompoundTag.of( - () -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId(DedicatedServer.getServer().registryAccess())) - ); + () -> (LinCompoundTag) toNativeLin( + blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()))); } @Nullable @@ -212,17 +215,16 @@ private synchronized boolean init() { if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { property = new BooleanProperty( state.getName(), - (List) ImmutableList.copyOf(state.getPossibleValues()) - ); + (List) ImmutableList.copyOf(state.getPossibleValues())); } else if (state instanceof DirectionProperty) { property = new DirectionalProperty( state.getName(), state .getPossibleValues() .stream() - .map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase())) - .collect(Collectors.toList()) - ); + .map(e -> Direction + .valueOf(((StringRepresentable) e).getSerializedName().toUpperCase())) + .collect(Collectors.toList())); } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { property = new EnumProperty( state.getName(), @@ -230,13 +232,11 @@ private synchronized boolean init() { .getPossibleValues() .stream() .map(e -> ((StringRepresentable) e).getSerializedName()) - .collect(Collectors.toList()) - ); + .collect(Collectors.toList())); } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { property = new IntegerProperty( state.getName(), - (List) ImmutableList.copyOf(state.getPossibleValues()) - ); + (List) ImmutableList.copyOf(state.getPossibleValues())); } else { throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + state .getClass() @@ -277,7 +277,8 @@ public BlockMaterial getMaterial(BlockType blockType) { @Override public synchronized BlockMaterial getMaterial(BlockState state) { - net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState(); + net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit + .createBlockData(state.getAsString())).getState(); return new PaperweightBlockMaterial(blockState.getBlock(), blockState); } @@ -328,7 +329,8 @@ public BaseBlock getFullBlock(final Location location) { // Read the NBT data BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK); if (blockEntity != null) { - net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()); + net.minecraft.nbt.CompoundTag tag = blockEntity + .saveWithId(DedicatedServer.getServer().registryAccess()); return state.toBaseBlock((LinCompoundTag) toNativeLin(tag)); } } @@ -340,8 +342,7 @@ public BaseBlock getFullBlock(final Location location) { SideEffect.HISTORY, SideEffect.HEIGHTMAPS, SideEffect.LIGHTING, - SideEffect.NEIGHBORS - ); + SideEffect.NEIGHBORS); @Override public Set getSupportedSideEffects() { @@ -361,24 +362,20 @@ public BaseEntity getEntity(org.bukkit.entity.Entity entity) { Entity mcEntity = craftEntity.getHandle(); String id = getEntityId(mcEntity); + EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id); + Supplier saveTag = () -> { + final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag(); + if (!readEntityIntoTag(mcEntity, minecraftTag)) { + return null; + } + // add Id for AbstractChangeSet to work + final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag); + final Map> tags = NbtUtils.getLinCompoundTagValues(tag); + tags.put("Id", LinStringTag.of(id)); + return LinCompoundTag.of(tags); + }; + return new LazyBaseEntity(type, saveTag); - if (id != null) { - EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id); - Supplier saveTag = () -> { - final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag(); - if (!readEntityIntoTag(mcEntity, minecraftTag)) { - return null; - } - //add Id for AbstractChangeSet to work - final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag); - final Map> tags = NbtUtils.getLinCompoundTagValues(tag); - tags.put("Id", LinStringTag.of(id)); - return LinCompoundTag.of(tags); - }; - return new LazyBaseEntity(type, saveTag); - } else { - return null; - } } @Override @@ -428,8 +425,7 @@ public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockSt return (char) ibdToOrdinal[id]; } catch (ArrayIndexOutOfBoundsException e1) { LOGGER.error("Attempted to convert {} with ID {} to char. ibdToOrdinal length: {}. Defaulting to air!", - blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1 - ); + blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1); return BlockTypesCache.ReservedIDs.AIR; } } @@ -502,18 +498,22 @@ public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockSt @Override public void sendFakeChunk(World world, Player player, ChunkPacket chunkPacket) { ServerLevel nmsWorld = getServerLevel(world); - ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); + ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), + chunkPacket.getChunkZ()); if (map != null && wasAccessibleSinceLastSave(map)) { boolean flag = false; // PlayerChunk.d players = map.players; - Stream stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag) - */ Stream.empty(); + Stream stream = /* + * players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), + * flag) + */ Stream.empty(); ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle(); stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer) .forEach(entityPlayer -> { synchronized (chunkPacket) { - ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket.getNativePacket(); + ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket + .getNativePacket(); if (nmsPacket == null) { nmsPacket = mapUtil.create(this, chunkPacket); chunkPacket.setNativePacket(nmsPacket); @@ -540,8 +540,7 @@ public boolean canPlaceAt(World world, BlockVector3 blockVector3, BlockState blo net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); return blockState1.hasPostProcess( getServerLevel(world), - new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z()) - ); + new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z())); } @Override @@ -550,9 +549,9 @@ public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) { ItemStack stack = new ItemStack( DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM) .get(ResourceLocation.tryParse(baseItemStack.getType().id())), - baseItemStack.getAmount() - ); - final net.minecraft.nbt.CompoundTag nbt = (net.minecraft.nbt.CompoundTag) fromNative(baseItemStack.getNbtData()); + baseItemStack.getAmount()); + final net.minecraft.nbt.CompoundTag nbt = (net.minecraft.nbt.CompoundTag) fromNative( + baseItemStack.getNbtData()); if (nbt != null) { final DataComponentPatch patch = COMPONENTS_CODEC .parse(registryAccess.createSerializationContext(NbtOps.INSTANCE), nbt) @@ -585,9 +584,22 @@ protected ServerLevel getServerLevel(final World world) { return ((CraftWorld) world).getHandle(); } + private T syncRegion(World world, BlockVector3 pt, java.util.function.Supplier supplier) { + if (FoliaLibHolder.isFolia()) { + Location location = new Location(world, pt.x(), pt.y(), pt.z()); + CompletableFuture future = new CompletableFuture<>(); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> future.complete(supplier.get())); + return future.join(); + } + return TaskManager.taskManager().sync(supplier); + } + @Override - public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, BlockVector3 pt) { - //FAWE start + public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, + BlockVector3 pt) { + // FAWE start ServerLevel serverLevel = getServerLevel(world); ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); @@ -597,15 +609,14 @@ public boolean generateFeature(ConfiguredFeatureType feature, World world, EditS .get(ResourceLocation.tryParse(feature.id())); FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { if (!configuredFeature.place( populator, generator, serverLevel.random, - new BlockPos(pt.x(), pt.y(), pt.z()) - )) { + new BlockPos(pt.x(), pt.y(), pt.z()))) { return null; } List placedBlocks = new ArrayList<>(populator.getList()); @@ -617,7 +628,7 @@ public boolean generateFeature(ConfiguredFeatureType feature, World world, EditS }); return placeFeatureIntoSession(editSession, populator, placed); - //FAWE end + // FAWE end } @Override @@ -635,9 +646,9 @@ public boolean generateStructure(StructureType type, World world, EditSession ed ChunkPos chunkPos = new ChunkPos(new BlockPos(pt.x(), pt.y(), pt.z())); - //FAWE start + // FAWE start FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { StructureStart structureStart = k.generate( @@ -650,20 +661,17 @@ public boolean generateStructure(StructureType type, World world, EditSession ed chunkPos, 0, populator, - biome -> true - ); + biome -> true); if (!structureStart.isValid()) { return null; } else { BoundingBox boundingBox = structureStart.getBoundingBox(); ChunkPos min = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.minX()), - SectionPos.blockToSectionCoord(boundingBox.minZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.minZ())); ChunkPos max = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.maxX()), - SectionPos.blockToSectionCoord(boundingBox.maxZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.maxZ())); ChunkPos.rangeClosed(min, max).forEach((chunkPosx) -> structureStart.placeInChunk( populator, serverLevel.structureManager(), @@ -675,10 +683,8 @@ public boolean generateStructure(StructureType type, World world, EditSession ed chunkPosx.getMinBlockZ(), chunkPosx.getMaxBlockX(), serverLevel.getMaxBuildHeight(), - chunkPosx.getMaxBlockZ() - ), - chunkPosx - )); + chunkPosx.getMaxBlockZ()), + chunkPosx)); List placedBlocks = new ArrayList<>(populator.getList()); placedBlocks.addAll(serverLevel.capturedBlockStates.values()); return placedBlocks; @@ -689,14 +695,13 @@ public boolean generateStructure(StructureType type, World world, EditSession ed }); return placeFeatureIntoSession(editSession, populator, placed); - //FAWE end + // FAWE end } private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, - final List placed - ) { + final List placed) { if (placed == null || placed.isEmpty()) { return false; } @@ -706,10 +711,12 @@ private boolean placeFeatureIntoSession( continue; } BlockPos pos = craftBlockState.getPosition(); - editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), BukkitAdapter.adapt(craftBlockState.getBlockData())); + editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), + BukkitAdapter.adapt(craftBlockState.getBlockData())); BlockEntity blockEntity = populator.getBlockEntity(pos); if (blockEntity != null) { - net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()); + net.minecraft.nbt.CompoundTag tag = blockEntity + .saveWithId(DedicatedServer.getServer().registryAccess()); editSession.setTile(pos.getX(), pos.getY(), pos.getZ(), (CompoundTag) toNative(tag)); } } @@ -722,7 +729,8 @@ public void setupFeatures() { // All these features should be the "face" selected Set face_features = Arrays - .stream(new Class[]{AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, VegetationFeatures.class}) + .stream(new Class[] { AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, + VegetationFeatures.class }) .flatMap(c -> Arrays.stream(c.getFields())) .filter(f -> { int modifiers = f.getModifiers(); @@ -776,16 +784,15 @@ public void setupFeatures() { public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess(); final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); - // We should be fine to perform this later as we're using a deep-copied itemstack (above) + // We should be fine to perform this later as we're using a deep-copied + // itemstack (above) final Supplier tag = () -> COMPONENTS_CODEC.encodeStart( registryAccess.createSerializationContext(NbtOps.INSTANCE), - nmsStack.getComponentsPatch() - ).getOrThrow(); + nmsStack.getComponentsPatch()).getOrThrow(); return new BaseItemStack( BukkitAdapter.asItemType(itemStack.getType()), LazyReference.from(() -> (LinCompoundTag) toNativeLin(tag.get())), - itemStack.getAmount() - ); + itemStack.getAmount()); } @Override diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweWorldNativeAccess.java index f7c2dc8e84..5bba575749 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweWorldNativeAccess.java @@ -21,6 +21,7 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.chunk.LevelChunk; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.event.block.BlockPhysicsEvent; @@ -59,7 +60,7 @@ public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAd this.level = level; // Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging. // - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway. - this.lastTick = new AtomicInteger(MinecraftServer.currentTick); + this.lastTick = new AtomicInteger(getCurrentTick()); } private Level getLevel() { @@ -95,7 +96,7 @@ public synchronized net.minecraft.world.level.block.state.BlockState setBlockSta LevelChunk levelChunk, BlockPos blockPos, net.minecraft.world.level.block.state.BlockState blockState ) { - int currentTick = MinecraftServer.currentTick; + int currentTick = getCurrentTick(); if (Fawe.isMainThread()) { return levelChunk.setBlockState(blockPos, blockState, this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE) @@ -290,4 +291,16 @@ private record CachedChange( } + private int getCurrentTick() { + try { + return MinecraftServer.currentTick; + } catch (NoSuchFieldError e) { + try { + return Bukkit.getCurrentTick(); + } catch (Exception ex) { + return 0; + } + } + } + } diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java index 436c28f981..e0c511f427 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java @@ -3,7 +3,6 @@ import com.fastasyncworldedit.bukkit.adapter.AbstractBukkitGetBlocks; import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; import com.fastasyncworldedit.bukkit.adapter.NativeEntityFunctionSet; -import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; @@ -12,7 +11,7 @@ import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkSet; -import com.fastasyncworldedit.core.queue.implementation.QueueHandler; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; @@ -59,6 +58,8 @@ import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.lighting.LevelLightEngine; import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.World; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.CraftBlock; @@ -82,7 +83,6 @@ import java.util.Map; import java.util.Set; import java.util.UUID; -import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -97,7 +97,8 @@ public class PaperweightGetBlocks extends AbstractBukkitGetBlocks posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); + private static final Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), + v.getZ()); public static final Function NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin .getInstance() .getBukkitImplAdapter()).blockEntityToCompoundTag(); @@ -170,8 +171,9 @@ public BiomeType getBiomeType(int x, int y, int z) { @Override public void removeSectionLighting(int layer, boolean sky) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK) + .getDataLayerData( + sectionPos); if (dataLayer != null) { lightUpdate = true; synchronized (dataLayer) { @@ -198,9 +200,8 @@ public void removeSectionLighting(int layer, boolean sky) { @Override 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))); + BlockEntity blockEntity = getChunk() + .getBlockEntity(new BlockPos((x & 15) + (chunkX << 4), y, (z & 15) + (chunkZ << 4))); if (blockEntity == null) { return null; } @@ -223,19 +224,19 @@ public int getSkyLight(int x, int y, int z) { int alayer = layer - getMinSectionPosition(); if (skyLight[alayer] == null) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = - serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getDataLayerData(sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY) + .getDataLayerData(sectionPos); // If the server hasn't generated the section's NibbleArray yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( LightLayer.BLOCK, sectionPos, - dataLayer - ); + dataLayer); } skyLight[alayer] = dataLayer; } @@ -257,12 +258,13 @@ public int getEmittedLight(int x, int y, int z) { // If the server hasn't generated the section's DataLayer yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); - ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, sectionPos, - dataLayer - ); + ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, + sectionPos, + dataLayer); } blockLight[alayer] = dataLayer; } @@ -335,8 +337,7 @@ protected > T internalCall( Runnable finalizer, int copyKey, LevelChunk nmsChunk, - ServerLevel nmsWorld - ) throws Exception { + ServerLevel nmsWorld) throws Exception { PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null; if (createCopy) { if (copies.containsKey(copyKey)) { @@ -366,7 +367,18 @@ protected > T internalCall( beacons = new ArrayList<>(); } beacons.add(tile); - PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + if (FoliaLibHolder.isFolia()) { + Location location = new Location( + nmsWorld.getWorld(), + tile.getBlockPos().getX(), + tile.getBlockPos().getY(), + tile.getBlockPos().getZ()); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk)); + } else { + PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + } continue; } nmsChunk.removeBlockEntity(tile.getBlockPos()); @@ -403,26 +415,25 @@ protected > T internalCall( } if (existingSection == null) { - PalettedContainer> biomeData = PaperweightPlatformAdapter.getBiomePalettedContainer( - biomes[setSectionIndex], - biomeHolderIdMap - ); + PalettedContainer> biomeData = PaperweightPlatformAdapter + .getBiomePalettedContainer( + biomes[setSectionIndex], + biomeHolderIdMap); LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, new char[4096], adapter, biomeRegistry, - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { - updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex); + getSectionIndex)) { + updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], + getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; @@ -431,8 +442,7 @@ protected > T internalCall( "Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); continue; } } @@ -440,8 +450,7 @@ protected > T internalCall( PalettedContainer> paletteBiomes = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); if (paletteBiomes != null) { PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes); } @@ -453,13 +462,16 @@ protected > T internalCall( bitMask |= 1 << getSectionIndex; - // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to - // this chunk GET when #updateGet is called. Future dords, please listen this time. + // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in + // order to write changes to + // this chunk GET when #updateGet is called. Future dords, please listen this + // time. char[] tmp = set.load(layerNo); char[] setArr = new char[tmp.length]; System.arraycopy(tmp, 0, setArr, 0, tmp.length); - // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was + // synchronise on internal section to avoid circular locking with a continuing + // edit if the chunk was // submitted to keep loaded internal chunks to queue target size. synchronized (super.sectionLocks[getSectionIndex]) { @@ -484,23 +496,22 @@ protected > T internalCall( PalettedContainer> biomeData = biomes == null ? new PalettedContainer<>( biomeHolderIdMap, biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap); + PalettedContainer.Strategy.SECTION_BIOMES) + : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], + biomeHolderIdMap); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, setArr, adapter, biomeRegistry, - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { + getSectionIndex)) { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); continue; } else { @@ -510,14 +521,14 @@ protected > T internalCall( "Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); continue; } } } - //ensure that the server doesn't try to tick the chunksection while we're editing it. (Again) + // ensure that the server doesn't try to tick the chunksection while we're + // editing it. (Again) PaperweightPlatformAdapter.clearCounts(existingSection); DelegateSemaphore lock = PaperweightPlatformAdapter.applyLock(existingSection); @@ -536,8 +547,10 @@ protected > T internalCall( this.reset(); } else if (!Arrays.equals(update(getSectionIndex, new char[4096], true), load(layerNo))) { this.reset(layerNo); - /*} else if (lock.isModified()) { - this.reset(layerNo);*/ + /* + * } else if (lock.isModified()) { + * this.reset(layerNo); + */ } } finally { sectionLock.writeLock().unlock(); @@ -546,8 +559,7 @@ protected > T internalCall( PalettedContainer> biomeData = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, @@ -555,22 +567,20 @@ protected > T internalCall( setArr, adapter, biomeRegistry, - biomeData != null ? biomeData : (PalettedContainer>) existingSection.getBiomes() - ); + biomeData != null ? biomeData + : (PalettedContainer>) existingSection.getBiomes()); if (!PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, existingSection, newSection, - getSectionIndex - )) { + getSectionIndex)) { LOGGER.error( "Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); } else { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); } @@ -582,14 +592,14 @@ protected > T internalCall( for (Map.Entry entry : heightMaps.entrySet()) { PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue()); } - PaperweightGetBlocks.this.setLightingToGet(set.getLight(), set.getMinSectionPosition(), set.getMaxSectionPosition()); + PaperweightGetBlocks.this.setLightingToGet(set.getLight(), set.getMinSectionPosition(), + set.getMaxSectionPosition()); PaperweightGetBlocks.this.setSkyLightingToGet( set.getSkyLight(), set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); + set.getMaxSectionPosition()); - Runnable[] syncTasks = null; + List syncTasks = new ArrayList<>(); int bx = chunkX << 4; int bz = chunkZ << 4; @@ -598,24 +608,18 @@ protected > T internalCall( // list will be null on spigot, so this is an implicit isPaper check if (beacons != null && !beacons.isEmpty()) { final List finalBeacons = beacons; - - syncTasks = new Runnable[4]; - - syncTasks[3] = () -> { + syncTasks.add(() -> { for (BlockEntity beacon : finalBeacons) { - BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), SoundEvents.BEACON_DEACTIVATE); + BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), + SoundEvents.BEACON_DEACTIVATE); new BeaconDeactivatedEvent(CraftBlock.at(beacon.getLevel(), beacon.getBlockPos())).callEvent(); } - }; + }); } Set entityRemoves = set.getEntityRemoves(); if (entityRemoves != null && !entityRemoves.isEmpty()) { - if (syncTasks == null) { - syncTasks = new Runnable[3]; - } - - syncTasks[2] = () -> { + syncTasks.add(() -> { Set entitiesRemoved = new HashSet<>(); final List entities = PaperweightPlatformAdapter.getEntities(nmsChunk); @@ -641,16 +645,12 @@ protected > T internalCall( // Only save entities that were actually removed to history set.getEntityRemoves().clear(); set.getEntityRemoves().addAll(entitiesRemoved); - }; + }); } Collection entities = set.entities(); if (entities != null && !entities.isEmpty()) { - if (syncTasks == null) { - syncTasks = new Runnable[2]; - } - - syncTasks[1] = () -> { + syncTasks.add(() -> { Iterator iterator = entities.iterator(); while (iterator.hasNext()) { final FaweCompoundTag nativeTag = iterator.next(); @@ -680,32 +680,44 @@ protected > T internalCall( entity.load(tag); entity.absMoveTo(x, y, z, yaw, pitch); entity.setUUID(NbtUtils.uuid(nativeTag)); - if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { - LOGGER.warn( - "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", - id, - nmsWorld.getWorld().getName(), - x, - y, - z - ); - // Unsuccessful create should not be saved to history - iterator.remove(); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } + }); + } else { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } } } } } - }; + }); } // set tiles Map tiles = set.tiles(); if (tiles != null && !tiles.isEmpty()) { - if (syncTasks == null) { - syncTasks = new Runnable[1]; - } - - syncTasks[0] = () -> { + syncTasks.add(() -> { for (final Map.Entry entry : tiles.entrySet()) { final FaweCompoundTag nativeTag = entry.getValue(); final BlockVector3 blockHash = entry.getKey(); @@ -714,22 +726,43 @@ protected > T internalCall( final int z = blockHash.z() + bz; final BlockPos pos = new BlockPos(x, y, z); - synchronized (nmsWorld) { - BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); - if (tileEntity == null || tileEntity.isRemoved()) { - nmsWorld.removeBlockEntity(pos); - tileEntity = nmsWorld.getBlockEntity(pos); - } - if (tileEntity != null) { - 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)); - tileEntity.loadWithComponents(tag, DedicatedServer.getServer().registryAccess()); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + tileEntity.loadWithComponents(tag, + DedicatedServer.getServer().registryAccess()); + } + } + }); + } else { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + tileEntity.loadWithComponents(tag, DedicatedServer.getServer().registryAccess()); + } } } } - }; + }); } Runnable callback; @@ -737,15 +770,18 @@ protected > T internalCall( callback = null; } else { int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0; - callback = () -> { + syncTasks.add(() -> { // Set Modified - nmsChunk.setLightCorrect(true); // Set Modified + nmsChunk.setLightCorrect(true); nmsChunk.mustNotSave = false; nmsChunk.setUnsaved(true); + }); + callback = () -> { // send to player if (!set .getSideEffectSet() - .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) { + .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING + || finalMask == 0 && biomes != null) { this.send(); } if (finalizer != null) { @@ -753,45 +789,8 @@ protected > T internalCall( } }; } - if (syncTasks != null) { - QueueHandler queueHandler = Fawe.instance().getQueueHandler(); - Runnable[] finalSyncTasks = syncTasks; - - // Chain the sync tasks and the callback - Callable chain = () -> { - try { - // Run the sync tasks - for (Runnable task : finalSyncTasks) { - if (task != null) { - task.run(); - } - } - if (callback == null) { - if (finalizer != null) { - queueHandler.async(finalizer, null); - } - return null; - } else { - return queueHandler.async(callback, null); - } - } catch (Throwable e) { - LOGGER.error("Error performing final chunk calling at {},{}", chunkX, chunkZ, e); - throw e; - } - }; - //noinspection unchecked - required at compile time - return (T) (Future) queueHandler.sync(chain); - } else { - if (callback == null) { - if (finalizer != null) { - finalizer.run(); - } - } else { - callback.run(); - } - } + return handleCallFinalizer(syncTasks, callback, finalizer); } - return null; } private void updateGet( @@ -799,8 +798,7 @@ private void updateGet( LevelChunkSection[] chunkSections, LevelChunkSection section, char[] arr, - int layer - ) { + int layer) { try { sectionLock.writeLock().lock(); if (this.getChunk() != nmsChunk) { @@ -814,8 +812,9 @@ private void updateGet( System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); } if (this.sections[layer] != section) { - // Not sure why it's funky, but it's what I did in commit fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords - this.sections[layer] = new LevelChunkSection[]{section}.clone()[0]; + // Not sure why it's funky, but it's what I did in commit + // fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords + this.sections[layer] = new LevelChunkSection[] { section }.clone()[0]; } } finally { sectionLock.writeLock().unlock(); @@ -831,14 +830,19 @@ public void send() { } /** - * Update a given (nullable) data array to the current data stored in the server's chunk, associated with this - * {@link PaperweightPlatformAdapter} instance. Not synchronised to the {@link PaperweightPlatformAdapter} instance as synchronisation - * is handled where necessary in the method, and should otherwise be handled correctly by this method's caller. + * Update a given (nullable) data array to the current data stored in the + * server's chunk, associated with this + * {@link PaperweightPlatformAdapter} instance. Not synchronised to the + * {@link PaperweightPlatformAdapter} instance as synchronisation + * is handled where necessary in the method, and should otherwise be handled + * correctly by this method's caller. * - * @param layer layer index (0 may denote a negative layer in the world, e.g. at y=-32) + * @param layer layer index (0 may denote a negative layer in the world, + * e.g. at y=-32) * @param data array to be updated/filled with data or null * @param aggressive if the cached section array should be re-acquired. - * @return the given array to be filled with data, or a new array if null is given. + * @return the given array to be filled with data, or a new array if null is + * given. */ @Override @SuppressWarnings("unchecked") @@ -868,7 +872,8 @@ public char[] update(int layer, char[] data, boolean aggressive) { return data; } - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get(dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get(dataObject); final int bitsPerEntry = bits.getBits(); final long[] blockStates = bits.getRaw(); @@ -948,10 +953,10 @@ public LevelChunk getChunk() { } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e); throw new FaweException( - TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), + TextComponent + .of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), FaweException.Type.OTHER, - false - ); + false); } } } @@ -959,14 +964,16 @@ public LevelChunk getChunk() { return levelChunk; } - private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, int maxSectionPosition) { + private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, + int maxSectionPosition) { for (int Y = 0; Y <= maxSectionPosition - minSectionPosition; Y++) { if (light[Y] == null) { continue; } SectionPos sectionPos = SectionPos.of(levelChunk.getPos(), Y + minSectionPosition); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer) + .getDataLayerData( + sectionPos); if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; Arrays.fill(LAYER_COUNT, lightLayer == LightLayer.SKY ? (byte) 15 : (byte) 0); @@ -974,8 +981,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( lightLayer, sectionPos, - dataLayer - ); + dataLayer); } synchronized (dataLayer) { for (int x = 0; x < 16; x++) { @@ -995,8 +1001,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti private PalettedContainer> setBiomesToPalettedContainer( final BiomeType[][] biomes, final int sectionIndex, - final PalettedContainerRO> data - ) { + final PalettedContainerRO> data) { BiomeType[] sectionBiomes; if (biomes == null || (sectionBiomes = biomes[sectionIndex]) == null) { return null; @@ -1013,8 +1018,7 @@ private PalettedContainer> setBiomesToPalettedContainer( x, y, z, - biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType)) - ); + biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType))); } } } @@ -1070,8 +1074,9 @@ public boolean trim(boolean aggressive) { final PalettedContainer blocksExisting = existing.getStates(); final Object dataObject = PaperweightPlatformAdapter.fieldData.get(blocksExisting); - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get( - dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get( + dataObject); int paletteSize; if (palette instanceof LinearPalette || palette instanceof HashMapPalette) { @@ -1081,7 +1086,8 @@ public boolean trim(boolean aggressive) { continue; } if (paletteSize == 1) { - //If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks. + // If the cached palette size is 1 then no blocks can have been changed i.e. do + // not need to update these chunks. continue; } super.trim(false, i); diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java index be8e7d734f..c9309fd27d 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java @@ -9,6 +9,7 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.bukkit.WorldEditPlugin; @@ -20,6 +21,7 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import io.papermc.lib.PaperLib; +import io.papermc.paper.util.MCUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.IdMap; @@ -129,13 +131,16 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c")); fieldPalette.setAccessible(true); - fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "g")); + fieldTickingFluidCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingFluidCount", "g")); fieldTickingFluidCount.setAccessible(true); - fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); + fieldTickingBlockCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); fieldTickingBlockCount.setAccessible(true); Field tmpFieldBiomes; try { - // Seems it's sometimes biomes and sometimes "i". Idk this is just easier than having to try to deal with it + // Seems it's sometimes biomes and sometimes "i". Idk this is just easier than + // having to try to deal with it tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("biomes"); // apparently unobf } catch (NoSuchFieldException ignored) { tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("i"); // apparently obf @@ -148,13 +153,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName( "getVisibleChunkIfPresent", - "b" - ), long.class); + "b"), long.class); getVisibleChunkIfPresent.setAccessible(true); methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent); if (!PaperLib.isPaper()) { - fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f")); + fieldThreadingDetector = PalettedContainer.class + .getDeclaredField(Refraction.pickName("threadingDetector", "f")); fieldThreadingDetector.setAccessible(true); fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); fieldLock.setAccessible(true); @@ -167,17 +172,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( Refraction.pickName("removeGameEventListener", "a"), BlockEntity.class, - ServerLevel.class - ); + ServerLevel.class); removeGameEventListener.setAccessible(true); methodRemoveGameEventListener = lookup.unreflect(removeGameEventListener); Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod( Refraction.pickName( "removeBlockEntityTicker", - "k" - ), BlockPos.class - ); + "k"), + BlockPos.class); removeBlockEntityTicker.setAccessible(true); methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker); @@ -190,14 +193,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { PAPER_CHUNK_GEN_ALL_ENTITIES.setAccessible(true); } catch (NoSuchMethodException ignored) { // Non-Paper - SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "N")); + SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class + .getDeclaredField(Refraction.pickName("entityManager", "N")); SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true); } Method palettedContaienrGet = PalettedContainer.class.getDeclaredMethod( Refraction.pickName("get", "a"), - int.class - ); + int.class); palettedContaienrGet.setAccessible(true); PALETTED_CONTAINER_GET = lookup.unreflect(palettedContaienrGet); } catch (RuntimeException | Error e) { @@ -213,14 +216,13 @@ static boolean setSectionAtomic( LevelChunkSection[] sections, LevelChunkSection expected, LevelChunkSection value, - int layer - ) { + int layer) { return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer); } // There is no point in having a functional semaphore for paper servers. - private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = - ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null)); + private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = ThreadLocal + .withInitial(() -> new DelegateSemaphore(1, null)); static DelegateSemaphore applyLock(LevelChunkSection section) { if (PaperLib.isPaper()) { @@ -275,11 +277,11 @@ public static CompletableFuture ensureLoaded(ServerLevel serverLevel "Unexpected error when getting completed future at chunk {},{}. Returning to default.", chunkX, chunkZ, - e - ); + e); } } - return CompletableFuture.supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); + return CompletableFuture + .supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); } private static LevelChunk toLevelChunk(Chunk chunk) { @@ -316,6 +318,14 @@ private static LevelChunk toLevelChunk(Chunk chunk) { } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { + if (FoliaLibHolder.isFolia()) { + try { + serverLevel.getChunkSource().addRegionTicket(ChunkHolderManager.UNLOAD_COOLDOWN, + new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE); + } catch (Exception ignored) { + } + return; + } // Ensure chunk is definitely loaded before applying a ticket io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel .getChunkSource() @@ -353,7 +363,7 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int if (lockHolder.chunkLock == null) { return; } - MinecraftServer.getServer().execute(() -> { + if (FoliaLibHolder.isFolia()) { try { ClientboundLevelChunkWithLightPacket packet; if (PaperLib.isPaper()) { @@ -370,14 +380,39 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int levelChunk, nmsWorld.getChunkSource().getLightEngine(), null, - null - ); + null); } nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet)); } finally { NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); } - }); + } else { + MinecraftServer.getServer().execute(() -> { + try { + ChunkPos pos = levelChunk.getPos(); + ClientboundLevelChunkWithLightPacket packet; + if (PaperLib.isPaper()) { + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null, + false // last false is to not bother with x-ray + ); + } else { + // deprecated on paper - deprecation suppressed + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null); + } + nearbyPlayers(nmsWorld, pos).forEach(p -> p.connection.send(packet)); + } finally { + NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); + } + }); + } } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { @@ -385,15 +420,14 @@ private static List nearbyPlayers(ServerLevel serverLevel, ChunkPo } /* - NMS conversion + * NMS conversion */ public static LevelChunkSection newChunkSection( final int layer, final char[] blocks, CachedBukkitAdapter adapter, Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { return newChunkSection(layer, null, blocks, adapter, biomeRegistry, biomes); } @@ -403,8 +437,7 @@ public static LevelChunkSection newChunkSection( char[] set, CachedBukkitAdapter adapter, Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (set == null) { return newChunkSection(biomeRegistry, biomes); } @@ -460,15 +493,15 @@ public static LevelChunkSection newChunkSection( } // Create palette with data - @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot - final PalettedContainer blockStatePalettedContainer = - new PalettedContainer<>( - Block.BLOCK_STATE_REGISTRY, - PalettedContainer.Strategy.SECTION_STATES, - PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, bitsPerEntry), - nmsBits, - palette - ); + @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility + // with spigot + final PalettedContainer blockStatePalettedContainer = new PalettedContainer<>( + Block.BLOCK_STATE_REGISTRY, + PalettedContainer.Strategy.SECTION_STATES, + PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, + bitsPerEntry), + nmsBits, + palette); if (biomes == null) { IdMap> biomeHolderIdMap = biomeRegistry.asHolderIdMap(); biomes = new PalettedContainer<>( @@ -478,8 +511,7 @@ public static LevelChunkSection newChunkSection( .getBukkitImplAdapter() .getInternalBiomeId( BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ); + PalettedContainer.Strategy.SECTION_BIOMES); } return new LevelChunkSection(blockStatePalettedContainer, biomes); @@ -496,16 +528,14 @@ public static LevelChunkSection newChunkSection( @SuppressWarnings("deprecation") // Only deprecated in paper private static LevelChunkSection newChunkSection( Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (biomes == null) { return new LevelChunkSection(biomeRegistry); } PalettedContainer dataPaletteBlocks = new PalettedContainer<>( Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), - PalettedContainer.Strategy.SECTION_STATES - ); + PalettedContainer.Strategy.SECTION_STATES); return new LevelChunkSection(dataPaletteBlocks, biomes); } @@ -518,17 +548,18 @@ public static void setBiomesToChunkSection(LevelChunkSection section, PalettedCo } /** - * Create a new {@link PalettedContainer}. Should only be used if no biome container existed beforehand. + * Create a new {@link PalettedContainer}. Should only be used if no + * biome container existed beforehand. */ public static PalettedContainer> getBiomePalettedContainer( BiomeType[] biomes, - IdMap> biomeRegistry - ) { + IdMap> biomeRegistry) { if (biomes == null) { return null; } BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - // Don't stream this as typically will see 1-4 biomes; stream overhead is large for the small length + // Don't stream this as typically will see 1-4 biomes; stream overhead is large + // for the small length Map> palette = new HashMap<>(); for (BiomeType biomeType : new LinkedList<>(Arrays.asList(biomes))) { Holder biome; @@ -543,16 +574,14 @@ public static PalettedContainer> getBiomePalettedContainer( int bitsPerEntry = MathMan.log2nlz(biomeCount - 1); Object configuration = PalettedContainer.Strategy.SECTION_STATES.getConfiguration( new FakeIdMapBiome(biomeCount), - bitsPerEntry - ); + bitsPerEntry); if (bitsPerEntry > 3) { bitsPerEntry = MathMan.log2nlz(biomeRegistry.size() - 1); } PalettedContainer> biomePalettedContainer = new PalettedContainer<>( biomeRegistry, biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ); + PalettedContainer.Strategy.SECTION_BIOMES); final Palette> biomePalette; if (bitsPerEntry == 0) { @@ -587,12 +616,11 @@ public static PalettedContainer> getBiomePalettedContainer( int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes final int arrayLength = MathMan.longArrayLength(bitsPerEntryNonZero, 64); - - BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) : new SimpleBitStorage( - bitsPerEntry, - 64, - new long[arrayLength] - ); + BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) + : new SimpleBitStorage( + bitsPerEntry, + 64, + new long[arrayLength]); try { Object data = dataConstructor.newInstance(configuration, bitStorage, biomePalette); @@ -659,17 +687,18 @@ static List getEntities(LevelChunk chunk) { return Optional.ofNullable(chunk.level .moonrise$getEntityLookup() .getChunk(chunk.locX, chunk.locZ)).map(c -> { - try { - //noinspection unchecked - return (List) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(c); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException("Failed to lookup entities [PAPER=true]", e); - } - }).orElse(Collections.emptyList()); + try { + // noinspection unchecked + return (List) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(c); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Failed to lookup entities [PAPER=true]", e); + } + }).orElse(Collections.emptyList()); } try { - //noinspection unchecked - return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos()); + // noinspection unchecked + return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))) + .getEntities(chunk.getPos()); } catch (IllegalAccessException e) { throw new RuntimeException("Failed to lookup entities [PAPER=false]", e); } diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/regen/PaperweightRegen.java index f62ae8e5d2..fe2d7eba6c 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/regen/PaperweightRegen.java @@ -1,6 +1,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.regen; import com.fastasyncworldedit.bukkit.adapter.Regenerator; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; @@ -42,6 +43,8 @@ import java.nio.file.Path; import java.util.Map; import java.util.OptionalLong; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.function.BooleanSupplier; import java.util.function.Supplier; @@ -53,14 +56,13 @@ public class PaperweightRegen extends Regenerator { private static final Field paperConfigField; private static final Field generatorSettingBaseSupplierField; - static { try { serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField.setAccessible(true); Field tmpPaperConfigField; - try { //only present on paper + try { // only present on paper tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField.setAccessible(true); } catch (Exception e) { @@ -76,7 +78,7 @@ public class PaperweightRegen extends Regenerator { } } - //runtime + // runtime private ServerLevel originalServerWorld; private ServerLevel freshWorld; private LevelStorageSource.LevelStorageAccess session; @@ -87,8 +89,7 @@ public PaperweightRegen( World originalBukkitWorld, Region region, Extent target, - RegenOptions options - ) { + RegenOptions options) { super(originalBukkitWorld, region, target, options); } @@ -110,10 +111,10 @@ protected boolean prepare() { @Override protected boolean initNewWorld() throws Exception { - //world folder + // world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); - //prepare for world init (see upstream implementation for reference) + // prepare for world init (see upstream implementation for reference) org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment(); org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator(); LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir); @@ -133,21 +134,19 @@ protected boolean initNewWorld() throws Exception { originalWorldData.settings.difficulty(), originalWorldData.settings.allowCommands(), originalWorldData.settings.gameRules(), - originalWorldData.settings.getDataConfiguration() - ); + originalWorldData.settings.getDataConfiguration()); - PrimaryLevelData.SpecialWorldProperty specialWorldProperty = - originalWorldData.isFlatWorld() - ? PrimaryLevelData.SpecialWorldProperty.FLAT - : originalWorldData.isDebugWorld() - ? PrimaryLevelData.SpecialWorldProperty.DEBUG - : PrimaryLevelData.SpecialWorldProperty.NONE; - PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable()); + PrimaryLevelData.SpecialWorldProperty specialWorldProperty = originalWorldData.isFlatWorld() + ? PrimaryLevelData.SpecialWorldProperty.FLAT + : originalWorldData.isDebugWorld() + ? PrimaryLevelData.SpecialWorldProperty.DEBUG + : PrimaryLevelData.SpecialWorldProperty.NONE; + PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, + Lifecycle.stable()); BiomeProvider biomeProvider = getBiomeProvider(); - - //init world + // init world freshWorld = Fawe.instance().getQueueHandler().sync((Supplier) () -> new ServerLevel( server, server.executor, @@ -156,8 +155,7 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.dimension(), new LevelStem( originalServerWorld.dimensionTypeRegistration(), - originalServerWorld.getChunkSource().getGenerator() - ), + originalServerWorld.getChunkSource().getGenerator()), new RegenNoOpWorldLoadListener(), originalServerWorld.isDebug(), seed, @@ -166,13 +164,14 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.getRandomSequences(), environment, generator, - biomeProvider - ) { + biomeProvider) { - private final Holder singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess() - .registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow( - WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) - ) : null; + private final Holder singleBiome = options.hasBiomeType() + ? DedicatedServer.getServer().registryAccess() + .registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow( + WorldEditPlugin.getInstance().getBukkitImplAdapter() + .getInternalBiomeId(options.getBiomeType())) + : null; @Override public @NotNull Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { @@ -186,8 +185,7 @@ protected boolean initNewWorld() throws Exception { public void save( @Nullable final ProgressListener progressListener, final boolean flush, - final boolean savingDisabled - ) { + final boolean savingDisabled) { // noop, spigot } @@ -196,20 +194,61 @@ public void save( @Nullable final ProgressListener progressListener, final boolean flush, final boolean savingDisabled, - final boolean close - ) { + final boolean close) { // noop, paper } }).get(); freshWorld.noSave = true; removeWorldFromWorldsMap(); - newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name + newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); // rename to original world name if (paperConfigField != null) { paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); } + + if (FoliaLibHolder.isFolia()) { + return initWorldForFolia(newWorldData); + } + return true; } + private boolean initWorldForFolia(PrimaryLevelData worldData) throws ExecutionException, InterruptedException { + MinecraftServer console = ((CraftServer) Bukkit.getServer()).getServer(); + + ChunkPos spawnChunk = new ChunkPos( + freshWorld.getChunkSource().randomState().sampler().findSpawnPosition()); + + setRandomSpawnSelection(spawnChunk); + + CompletableFuture initFuture = new CompletableFuture<>(); + + org.bukkit.Location spawnLocation = new org.bukkit.Location( + freshWorld.getWorld(), + spawnChunk.x << 4, + 64, + spawnChunk.z << 4); + FoliaLibHolder.getScheduler().runAtLocation(spawnLocation, task -> { + try { + console.initWorld(freshWorld, worldData, worldData, worldData.worldGenOptions()); + initFuture.complete(true); + } catch (Exception e) { + initFuture.completeExceptionally(e); + } + }); + + return initFuture.get(); + } + + private void setRandomSpawnSelection(ChunkPos spawnChunk) { + try { + Field randomSpawnField = ServerLevel.class.getDeclaredField("randomSpawnSelection"); + randomSpawnField.setAccessible(true); + randomSpawnField.set(freshWorld, spawnChunk); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to set randomSpawnSelection for Folia world initialization", e); + } + } + @Override protected void cleanup() { try { @@ -217,7 +256,7 @@ protected void cleanup() { } catch (Exception ignored) { } - //shutdown chunk provider + // shutdown chunk provider try { Fawe.instance().getQueueHandler().sync(() -> { try { @@ -230,29 +269,35 @@ protected void cleanup() { } catch (Exception ignored) { } - //remove world from server + // remove world from server try { Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap); } catch (Exception ignored) { } - //delete directory + // delete directory try { SafeFiles.tryHardToDeleteDir(tempDir); } catch (Exception ignored) { } } + @Override + protected World getFreshWorld() { + return freshWorld != null ? freshWorld.getWorld() : null; + } + @Override protected IChunkCache initSourceQueueCache() { return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } - //util + // util @SuppressWarnings("unchecked") private void removeWorldFromWorldsMap() { try { - Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + Map map = (Map) serverWorldsField + .get(Bukkit.getServer()); map.remove("faweregentempworld"); } catch (IllegalAccessException e) { throw new RuntimeException(e); @@ -279,8 +324,7 @@ public void updateSpawnPos(@NotNull ChunkPos spawnPos) { @Override public void onStatusChange( final @NotNull ChunkPos pos, - @org.jetbrains.annotations.Nullable final net.minecraft.world.level.chunk.status.ChunkStatus status - ) { + @org.jetbrains.annotations.Nullable final net.minecraft.world.level.chunk.status.ChunkStatus status) { } diff --git a/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightFaweAdapter.java index 3c8b0c6c09..ac6e79f53b 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightFaweAdapter.java @@ -10,6 +10,7 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -96,6 +97,7 @@ import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; import org.bukkit.Location; +import java.util.concurrent.CompletableFuture; import org.bukkit.NamespacedKey; import org.bukkit.World; import org.bukkit.block.data.BlockData; @@ -140,12 +142,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter COMPONENTS_CODEC = DataComponentPatch.CODEC.optionalFieldOf( - "components", DataComponentPatch.EMPTY - ).codec(); + "components", DataComponentPatch.EMPTY).codec(); static { try { - CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave"); + CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class + .getDeclaredMethod("wasAccessibleSinceLastSave"); } catch (NoSuchMethodException ignored) { // may not be present in newer paper versions } } @@ -162,29 +164,30 @@ public Function blockEntityToCompoundTag() { LinValueOutput output = createOutput(); blockEntity.saveWithId(output); return output.buildResult(); - } - ); + }); } public static Property adaptProperty(net.minecraft.world.level.block.state.properties.Property property) { return switch (property) { case net.minecraft.world.level.block.state.properties.BooleanProperty booleanProperty -> - new BooleanProperty(booleanProperty.getName(), ImmutableList.copyOf(booleanProperty.getPossibleValues())); + new BooleanProperty(booleanProperty.getName(), + ImmutableList.copyOf(booleanProperty.getPossibleValues())); case net.minecraft.world.level.block.state.properties.IntegerProperty integerProperty -> - new IntegerProperty(integerProperty.getName(), ImmutableList.copyOf(integerProperty.getPossibleValues())); + new IntegerProperty(integerProperty.getName(), + ImmutableList.copyOf(integerProperty.getPossibleValues())); case net.minecraft.world.level.block.state.properties.EnumProperty enumProperty -> { if (enumProperty.getValueClass() == net.minecraft.core.Direction.class) { yield new DirectionalProperty(enumProperty.getName(), enumProperty.getPossibleValues().stream() .map(StringRepresentable::getSerializedName) .map(s -> s.toUpperCase(Locale.ROOT)) .map(Direction::valueOf) - .toList() - ); + .toList()); } yield new EnumProperty(enumProperty.getName(), enumProperty.getPossibleValues().stream() .map(StringRepresentable::getSerializedName).collect(Collectors.toCollection(ArrayList::new))); } - default -> throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + property.getClass().getSimpleName()); + default -> throw new IllegalArgumentException( + "FastAsyncWorldEdit needs an update to support " + property.getClass().getSimpleName()); }; } @@ -261,7 +264,8 @@ public BlockMaterial getMaterial(BlockType blockType) { @Override public synchronized BlockMaterial getMaterial(BlockState state) { - net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState(); + net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit + .createBlockData(state.getAsString())).getState(); return new PaperweightBlockMaterial(blockState.getBlock(), blockState); } @@ -324,8 +328,7 @@ public BaseBlock getFullBlock(final Location location) { SideEffect.HISTORY, SideEffect.HEIGHTMAPS, SideEffect.LIGHTING, - SideEffect.NEIGHBORS - ); + SideEffect.NEIGHBORS); @Override public Set getSupportedSideEffects() { @@ -351,7 +354,7 @@ public BaseEntity getEntity(org.bukkit.entity.Entity entity) { if (!mcEntity.save(output)) { return null; } - //add Id for AbstractChangeSet to work + // add Id for AbstractChangeSet to work return output.toBuilder().putString("Id", id).build(); }; return new LazyBaseEntity(type, saveTag); @@ -404,8 +407,7 @@ public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockSt return (char) ibdToOrdinal[id]; } catch (ArrayIndexOutOfBoundsException e1) { LOGGER.error("Attempted to convert {} with ID {} to char. ibdToOrdinal length: {}. Defaulting to air!", - blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1 - ); + blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1); return BlockTypesCache.ReservedIDs.AIR; } } @@ -478,18 +480,22 @@ public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockSt @Override public void sendFakeChunk(World world, Player player, ChunkPacket chunkPacket) { ServerLevel nmsWorld = getServerLevel(world); - ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); + ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), + chunkPacket.getChunkZ()); if (map != null && wasAccessibleSinceLastSave(map)) { boolean flag = false; // PlayerChunk.d players = map.players; - Stream stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag) - */ Stream.empty(); + Stream stream = /* + * players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), + * flag) + */ Stream.empty(); ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle(); stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer) .forEach(entityPlayer -> { synchronized (chunkPacket) { - ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket.getNativePacket(); + ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket + .getNativePacket(); if (nmsPacket == null) { nmsPacket = mapUtil.create(this, chunkPacket); chunkPacket.setNativePacket(nmsPacket); @@ -516,8 +522,7 @@ public boolean canPlaceAt(World world, BlockVector3 blockVector3, BlockState blo net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); return blockState1.hasPostProcess( getServerLevel(world), - new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z()) - ); + new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z())); } @Override @@ -525,10 +530,8 @@ public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) { final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess(); ItemStack stack = new ItemStack( registryAccess.lookupOrThrow(Registries.ITEM).getValueOrThrow(ResourceKey.create( - Registries.ITEM, Identifier.parse(baseItemStack.getType().id()) - )), - baseItemStack.getAmount() - ); + Registries.ITEM, Identifier.parse(baseItemStack.getType().id()))), + baseItemStack.getAmount()); final CompoundTag nbt = (CompoundTag) fromNativeLin(baseItemStack.getNbt()); if (nbt != null) { final DataComponentPatch patch = COMPONENTS_CODEC @@ -556,8 +559,22 @@ protected void postCaptureBlockStates(final ServerLevel serverLevel) { serverLevel.captureTreeGeneration = false; serverLevel.capturedBlockStates.clear(); } + + private T syncRegion(World world, BlockVector3 pt, java.util.function.Supplier supplier) { + if (FoliaLibHolder.isFolia()) { + Location location = new Location(world, pt.x(), pt.y(), pt.z()); + CompletableFuture future = new CompletableFuture<>(); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> future.complete(supplier.get())); + return future.join(); + } + return TaskManager.taskManager().sync(supplier); + } + @Override - public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, BlockVector3 pt) { + public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, + BlockVector3 pt) { ServerLevel serverLevel = getServerLevel(world); ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); @@ -567,15 +584,14 @@ public boolean generateFeature(ConfiguredFeatureType feature, World world, EditS .getValue(Identifier.tryParse(feature.id())); FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { if (!configuredFeature.place( populator, generator, serverLevel.random, - new BlockPos(pt.x(), pt.y(), pt.z()) - )) { + new BlockPos(pt.x(), pt.y(), pt.z()))) { return null; } List placedBlocks = new ArrayList<>(populator.getSnapshotBlocks()); @@ -604,7 +620,7 @@ public boolean generateStructure(StructureType type, World world, EditSession ed TransformerGeneratorAccess access = new TransformerGeneratorAccess(); FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); access.setDelegate(populator); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { StructureStart structureStart = structure.generate( @@ -619,20 +635,17 @@ public boolean generateStructure(StructureType type, World world, EditSession ed chunkPos, 0, populator, - biome -> true - ); + biome -> true); if (!structureStart.isValid()) { return null; } else { BoundingBox boundingBox = structureStart.getBoundingBox(); ChunkPos min = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.minX()), - SectionPos.blockToSectionCoord(boundingBox.minZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.minZ())); ChunkPos max = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.maxX()), - SectionPos.blockToSectionCoord(boundingBox.maxZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.maxZ())); ChunkPos.rangeClosed(min, max).forEach((chunkPosx) -> structureStart.placeInChunk( access, serverLevel.structureManager(), @@ -643,9 +656,8 @@ public boolean generateStructure(StructureType type, World world, EditSession ed serverLevel.getMinY(), chunkPosx.getMinBlockZ(), chunkPosx.getMaxBlockX(), - serverLevel.getMaxY(), chunkPosx.getMaxBlockZ() - ), chunkPosx - )); + serverLevel.getMaxY(), chunkPosx.getMaxBlockZ()), + chunkPosx)); List placedBlocks = new ArrayList<>(populator.getSnapshotBlocks()); placedBlocks.addAll(serverLevel.capturedBlockStates.values()); return placedBlocks; @@ -661,8 +673,7 @@ public boolean generateStructure(StructureType type, World world, EditSession ed private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, - final List placed - ) { + final List placed) { if (placed == null || placed.isEmpty()) { return false; } @@ -672,7 +683,8 @@ private boolean placeFeatureIntoSession( continue; } BlockPos pos = craftBlockState.getPosition(); - editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), BukkitAdapter.adapt(craftBlockState.getBlockData())); + editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), + BukkitAdapter.adapt(craftBlockState.getBlockData())); BlockEntity blockEntity = populator.getBlockEntity(pos); if (blockEntity != null) { LinValueOutput output = createOutput(); @@ -689,7 +701,8 @@ public void setupFeatures() { // All these features should be the "face" selected Set face_features = Arrays - .stream(new Class[]{AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, VegetationFeatures.class}) + .stream(new Class[] { AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, + VegetationFeatures.class }) .flatMap(c -> Arrays.stream(c.getFields())) .filter(f -> { int modifiers = f.getModifiers(); @@ -748,16 +761,15 @@ protected ServerLevel getServerLevel(final World world) { public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess(); final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); - // We should be fine to perform this later as we're using a deep-copied itemstack (above) + // We should be fine to perform this later as we're using a deep-copied + // itemstack (above) final Supplier tag = () -> COMPONENTS_CODEC.encodeStart( registryAccess.createSerializationContext(NbtOps.INSTANCE), - nmsStack.getComponentsPatch() - ).getOrThrow(); + nmsStack.getComponentsPatch()).getOrThrow(); return new BaseItemStack( BukkitAdapter.asItemType(itemStack.getType()), LazyReference.from(() -> (LinCompoundTag) toNativeLin(tag.get())), - itemStack.getAmount() - ); + itemStack.getAmount()); } @Override diff --git a/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightGetBlocks.java index 7bdd4a7f4b..68e7fbce6a 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightGetBlocks.java @@ -11,6 +11,7 @@ import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; @@ -55,6 +56,7 @@ import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.storage.ValueInput; import org.apache.logging.log4j.Logger; +import org.bukkit.Location; import org.bukkit.World; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.CraftBlock; @@ -94,7 +96,8 @@ public class PaperweightGetBlocks extends AbstractBukkitGetBlocks posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); + private static final Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), + v.getZ()); public static final Function NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin .getInstance() .getBukkitImplAdapter()).blockEntityToCompoundTag(); @@ -167,8 +170,9 @@ public BiomeType getBiomeType(int x, int y, int z) { @Override public void removeSectionLighting(int layer, boolean sky) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK) + .getDataLayerData( + sectionPos); if (dataLayer != null) { lightUpdate = true; synchronized (dataLayer) { @@ -195,9 +199,8 @@ public void removeSectionLighting(int layer, boolean sky) { @Override 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))); + BlockEntity blockEntity = getChunk() + .getBlockEntity(new BlockPos((x & 15) + (chunkX << 4), y, (z & 15) + (chunkZ << 4))); if (blockEntity == null) { return null; } @@ -220,19 +223,19 @@ public int getSkyLight(int x, int y, int z) { int alayer = layer - getMinSectionPosition(); if (skyLight[alayer] == null) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = - serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getDataLayerData(sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY) + .getDataLayerData(sectionPos); // If the server hasn't generated the section's NibbleArray yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( LightLayer.BLOCK, sectionPos, - dataLayer - ); + dataLayer); } skyLight[alayer] = dataLayer; } @@ -254,12 +257,13 @@ public int getEmittedLight(int x, int y, int z) { // If the server hasn't generated the section's DataLayer yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); - ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, sectionPos, - dataLayer - ); + ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, + sectionPos, + dataLayer); } blockLight[alayer] = dataLayer; } @@ -332,8 +336,7 @@ protected > T internalCall( Runnable finalizer, int copyKey, LevelChunk nmsChunk, - ServerLevel nmsWorld - ) throws Exception { + ServerLevel nmsWorld) throws Exception { PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null; if (createCopy) { if (copies.containsKey(copyKey)) { @@ -363,7 +366,18 @@ protected > T internalCall( beacons = new ArrayList<>(); } beacons.add(tile); - PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + if (FoliaLibHolder.isFolia()) { + Location location = new Location( + nmsWorld.getWorld(), + tile.getBlockPos().getX(), + tile.getBlockPos().getY(), + tile.getBlockPos().getZ()); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk)); + } else { + PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + } continue; } nmsChunk.removeBlockEntity(tile.getBlockPos()); @@ -400,33 +414,32 @@ protected > T internalCall( } if (existingSection == null) { - PalettedContainer> biomeData = PaperweightPlatformAdapter.getBiomePalettedContainer( - biomes[setSectionIndex], - biomeHolderIdMap - ); + PalettedContainer> biomeData = PaperweightPlatformAdapter + .getBiomePalettedContainer( + biomes[setSectionIndex], + biomeHolderIdMap); LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, new char[4096], adapter, serverLevel.registryAccess(), - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { - updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex); + getSectionIndex)) { + updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], + getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; if (existingSection == null) { - LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, + chunkZ, + getSectionIndex); continue; } } @@ -434,8 +447,7 @@ protected > T internalCall( PalettedContainer> paletteBiomes = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); if (paletteBiomes != null) { PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes); } @@ -447,13 +459,16 @@ protected > T internalCall( bitMask |= 1 << getSectionIndex; - // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to - // this chunk GET when #updateGet is called. Future dords, please listen this time. + // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in + // order to write changes to + // this chunk GET when #updateGet is called. Future dords, please listen this + // time. char[] tmp = set.load(layerNo); char[] setArr = new char[tmp.length]; System.arraycopy(tmp, 0, setArr, 0, tmp.length); - // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was + // synchronise on internal section to avoid circular locking with a continuing + // edit if the chunk was // submitted to keep loaded internal chunks to queue target size. synchronized (super.sectionLocks[getSectionIndex]) { @@ -476,38 +491,37 @@ protected > T internalCall( if (existingSection == null) { - PalettedContainer> biomeData = biomes == null ? - serverLevel.palettedContainerFactory().createForBiomes() : - PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap); + PalettedContainer> biomeData = biomes == null + ? serverLevel.palettedContainerFactory().createForBiomes() + : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], + biomeHolderIdMap); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, setArr, adapter, serverLevel.registryAccess(), - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { + getSectionIndex)) { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; if (existingSection == null) { LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); continue; } } } - //ensure that the server doesn't try to tick the chunksection while we're editing it. (Again) + // ensure that the server doesn't try to tick the chunksection while we're + // editing it. (Again) PaperweightPlatformAdapter.clearCounts(existingSection); DelegateSemaphore lock = PaperweightPlatformAdapter.applyLock(existingSection); @@ -526,11 +540,12 @@ protected > T internalCall( this.reset(); } else if (!Arrays.equals( update(getSectionIndex, new char[4096], true), - load(layerNo) - )) { + load(layerNo))) { this.reset(layerNo); - /*} else if (lock.isModified()) { - this.reset(layerNo);*/ + /* + * } else if (lock.isModified()) { + * this.reset(layerNo); + */ } } finally { sectionLock.writeLock().unlock(); @@ -539,8 +554,7 @@ protected > T internalCall( PalettedContainer> biomeData = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, @@ -548,19 +562,17 @@ protected > T internalCall( setArr, adapter, serverLevel.registryAccess(), - biomeData != null ? biomeData : (PalettedContainer>) existingSection.getBiomes() - ); + biomeData != null ? biomeData + : (PalettedContainer>) existingSection.getBiomes()); if (!PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, existingSection, newSection, - getSectionIndex - )) { + getSectionIndex)) { LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); } else { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); } @@ -575,13 +587,11 @@ protected > T internalCall( PaperweightGetBlocks.this.setLightingToGet( set.getLight(), set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); + set.getMaxSectionPosition()); PaperweightGetBlocks.this.setSkyLightingToGet( set.getSkyLight(), set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); + set.getMaxSectionPosition()); List syncTasks = new ArrayList<>(); @@ -595,7 +605,8 @@ protected > T internalCall( syncTasks.add(() -> { for (BlockEntity beacon : finalBeacons) { - BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), SoundEvents.BEACON_DEACTIVATE); + BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), + SoundEvents.BEACON_DEACTIVATE); new BeaconDeactivatedEvent(CraftBlock.at(beacon.getLevel(), beacon.getBlockPos())).callEvent(); } }); @@ -667,17 +678,33 @@ protected > T internalCall( entity.load(input); entity.absSnapTo(x, y, z, yaw, pitch); entity.setUUID(NbtUtils.uuid(nativeTag)); - if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { - LOGGER.warn( - "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", - id, - nmsWorld.getWorld().getName(), - x, - y, - z - ); - // Unsuccessful create should not be saved to history - iterator.remove(); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } + }); + } else { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } } } } @@ -698,18 +725,36 @@ protected > T internalCall( final int z = blockHash.z() + bz; final BlockPos pos = new BlockPos(x, y, z); - synchronized (nmsWorld) { - BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); - if (tileEntity == null || tileEntity.isRemoved()) { - nmsWorld.removeBlockEntity(pos); - tileEntity = nmsWorld.getBlockEntity(pos); - } - if (tileEntity != null) { - ValueInput input = createInput(nativeTag.linTag().toBuilder() - .putInt("x", x).putInt("y", y).putInt("z", z) - .build() - ); - tileEntity.loadWithComponents(input); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + ValueInput input = createInput(nativeTag.linTag().toBuilder() + .putInt("x", x).putInt("y", y).putInt("z", z) + .build()); + tileEntity.loadWithComponents(input); + } + } + }); + } else { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + ValueInput input = createInput(nativeTag.linTag().toBuilder() + .putInt("x", x).putInt("y", y).putInt("z", z) + .build()); + tileEntity.loadWithComponents(input); + } } } } @@ -730,7 +775,8 @@ protected > T internalCall( // send to player if (!set .getSideEffectSet() - .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) { + .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING + || finalMask == 0 && biomes != null) { this.send(); } if (finalizer != null) { @@ -747,8 +793,7 @@ private void updateGet( LevelChunkSection[] chunkSections, LevelChunkSection section, char[] arr, - int layer - ) { + int layer) { try { sectionLock.writeLock().lock(); if (this.getChunk() != nmsChunk) { @@ -762,8 +807,9 @@ private void updateGet( System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); } if (this.sections[layer] != section) { - // Not sure why it's funky, but it's what I did in commit fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords - this.sections[layer] = new LevelChunkSection[]{section}.clone()[0]; + // Not sure why it's funky, but it's what I did in commit + // fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords + this.sections[layer] = new LevelChunkSection[] { section }.clone()[0]; } } finally { sectionLock.writeLock().unlock(); @@ -779,14 +825,19 @@ public void send() { } /** - * Update a given (nullable) data array to the current data stored in the server's chunk, associated with this - * {@link PaperweightPlatformAdapter} instance. Not synchronised to the {@link PaperweightPlatformAdapter} instance as synchronisation - * is handled where necessary in the method, and should otherwise be handled correctly by this method's caller. + * Update a given (nullable) data array to the current data stored in the + * server's chunk, associated with this + * {@link PaperweightPlatformAdapter} instance. Not synchronised to the + * {@link PaperweightPlatformAdapter} instance as synchronisation + * is handled where necessary in the method, and should otherwise be handled + * correctly by this method's caller. * - * @param layer layer index (0 may denote a negative layer in the world, e.g. at y=-32) + * @param layer layer index (0 may denote a negative layer in the world, + * e.g. at y=-32) * @param data array to be updated/filled with data or null * @param aggressive if the cached section array should be re-acquired. - * @return the given array to be filled with data, or a new array if null is given. + * @return the given array to be filled with data, or a new array if null is + * given. */ @Override @SuppressWarnings("unchecked") @@ -816,7 +867,8 @@ public char[] update(int layer, char[] data, boolean aggressive) { return data; } - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get(dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get(dataObject); final int bitsPerEntry = bits.getBits(); final long[] blockStates = bits.getRaw(); @@ -896,10 +948,10 @@ public LevelChunk getChunk() { } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e); throw new FaweException( - TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), + TextComponent + .of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), FaweException.Type.OTHER, - false - ); + false); } } } @@ -907,14 +959,16 @@ public LevelChunk getChunk() { return levelChunk; } - private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, int maxSectionPosition) { + private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, + int maxSectionPosition) { for (int Y = 0; Y <= maxSectionPosition - minSectionPosition; Y++) { if (light[Y] == null) { continue; } SectionPos sectionPos = SectionPos.of(levelChunk.getPos(), Y + minSectionPosition); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer) + .getDataLayerData( + sectionPos); if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; Arrays.fill(LAYER_COUNT, lightLayer == LightLayer.SKY ? (byte) 15 : (byte) 0); @@ -922,8 +976,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( lightLayer, sectionPos, - dataLayer - ); + dataLayer); } synchronized (dataLayer) { for (int x = 0; x < 16; x++) { @@ -943,8 +996,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti private PalettedContainer> setBiomesToPalettedContainer( final BiomeType[][] biomes, final int sectionIndex, - final PalettedContainerRO> data - ) { + final PalettedContainerRO> data) { BiomeType[] sectionBiomes; if (biomes == null || (sectionBiomes = biomes[sectionIndex]) == null) { return null; @@ -961,8 +1013,7 @@ private PalettedContainer> setBiomesToPalettedContainer( x, y, z, - biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType)) - ); + biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType))); } } } @@ -1018,8 +1069,9 @@ public boolean trim(boolean aggressive) { final PalettedContainer blocksExisting = existing.getStates(); final Object dataObject = PaperweightPlatformAdapter.fieldData.get(blocksExisting); - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get( - dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get( + dataObject); int paletteSize; if (palette instanceof LinearPalette || palette instanceof HashMapPalette) { @@ -1029,7 +1081,8 @@ public boolean trim(boolean aggressive) { continue; } if (paletteSize == 1) { - //If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks. + // If the cached palette size is 1 then no blocks can have been changed i.e. do + // not need to update these chunks. continue; } super.trim(false, i); diff --git a/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightPlatformAdapter.java index 1f1fab622e..d37f4bf0f9 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightPlatformAdapter.java @@ -9,6 +9,7 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.serialization.DataResult; @@ -123,23 +124,25 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c")); fieldPalette.setAccessible(true); - //noinspection JavaLangInvokeHandleSignature - method is obfuscated - palettedContainerUnpackSpigot = PaperLib.isPaper() ? null : lookup.findStatic( - PalettedContainer.class, - "a", // unpack - MethodType.methodType(DataResult.class, Strategy.class, PalettedContainerRO.PackedData.class) - ); + // noinspection JavaLangInvokeHandleSignature - method is obfuscated + palettedContainerUnpackSpigot = PaperLib.isPaper() ? null + : lookup.findStatic( + PalettedContainer.class, + "a", // unpack + MethodType.methodType(DataResult.class, Strategy.class, + PalettedContainerRO.PackedData.class)); fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName( "tickingFluidCount", - "g" - )); + "g")); fieldTickingFluidCount.setAccessible(true); - fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); + fieldTickingBlockCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); fieldTickingBlockCount.setAccessible(true); Field tmpFieldBiomes; try { - // Seems it's sometimes biomes and sometimes "i". Idk this is just easier than having to try to deal with it + // Seems it's sometimes biomes and sometimes "i". Idk this is just easier than + // having to try to deal with it tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("biomes"); // apparently unobf } catch (NoSuchFieldException ignored) { tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("i"); // apparently obf @@ -150,18 +153,19 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod( Refraction.pickName( "getVisibleChunkIfPresent", - "b" - ), long.class - ); + "b"), + long.class); getVisibleChunkIfPresent.setAccessible(true); methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent); if (!PaperLib.isPaper()) { - fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "d")); + fieldThreadingDetector = PalettedContainer.class + .getDeclaredField(Refraction.pickName("threadingDetector", "d")); fieldThreadingDetector.setAccessible(true); fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); fieldLock.setAccessible(true); - SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "M")); + SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class + .getDeclaredField(Refraction.pickName("entityManager", "M")); SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true); } else { // in paper, the used methods are synchronized properly @@ -172,17 +176,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( Refraction.pickName("removeGameEventListener", "a"), BlockEntity.class, - ServerLevel.class - ); + ServerLevel.class); removeGameEventListener.setAccessible(true); methodRemoveGameEventListener = lookup.unreflect(removeGameEventListener); Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod( Refraction.pickName( "removeBlockEntityTicker", - "k" - ), BlockPos.class - ); + "k"), + BlockPos.class); removeBlockEntityTicker.setAccessible(true); methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker); @@ -191,8 +193,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method palettedContainerGet = PalettedContainer.class.getDeclaredMethod( Refraction.pickName("get", "a"), - int.class - ); + int.class); palettedContainerGet.setAccessible(true); PALETTED_CONTAINER_GET = lookup.unreflect(palettedContainerGet); } catch (RuntimeException | Error e) { @@ -205,16 +206,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { public static LinValueOutput createOutput() { return LinValueOutput.createWithContext( ProblemReporter.DISCARDING, - DedicatedServer.getServer().registryAccess() - ); + DedicatedServer.getServer().registryAccess()); } public static LinValueInput createInput(LinCompoundTag input) { return LinValueInput.create( ProblemReporter.DISCARDING, DedicatedServer.getServer().registryAccess(), - input - ); + input); } static boolean setSectionAtomic( @@ -223,14 +222,13 @@ static boolean setSectionAtomic( LevelChunkSection[] sections, LevelChunkSection expected, LevelChunkSection value, - int layer - ) { + int layer) { return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer); } // There is no point in having a functional semaphore for paper servers. - private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = - ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null)); + private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = ThreadLocal + .withInitial(() -> new DelegateSemaphore(1, null)); static DelegateSemaphore applyLock(LevelChunkSection section) { if (PaperLib.isPaper()) { @@ -285,11 +283,11 @@ public static CompletableFuture ensureLoaded(ServerLevel serverLevel "Unexpected error when getting completed future at chunk {},{}. Returning to default.", chunkX, chunkZ, - e - ); + e); } } - return CompletableFuture.supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); + return CompletableFuture + .supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); } private static LevelChunk toLevelChunk(Chunk chunk) { @@ -326,6 +324,14 @@ private static LevelChunk toLevelChunk(Chunk chunk) { } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { + if (FoliaLibHolder.isFolia()) { + try { + serverLevel.getChunkSource().addTicketWithRadius(ChunkHolderManager.UNLOAD_COOLDOWN, + new ChunkPos(chunkX, chunkZ), 0); + } catch (Exception ignored) { + } + return; + } // Ensure chunk is definitely loaded before applying a ticket io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel .getChunkSource() @@ -362,7 +368,7 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int if (lockHolder.chunkLock == null) { return; } - MinecraftServer.getServer().execute(() -> { + if (FoliaLibHolder.isFolia()) { try { ChunkPos pos = levelChunk.getPos(); ClientboundLevelChunkWithLightPacket packet; @@ -380,14 +386,39 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int levelChunk, nmsWorld.getLightEngine(), null, - null - ); + null); } nearbyPlayers(nmsWorld, pos).forEach(p -> p.connection.send(packet)); } finally { NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); } - }); + } else { + MinecraftServer.getServer().execute(() -> { + try { + ChunkPos pos = levelChunk.getPos(); + ClientboundLevelChunkWithLightPacket packet; + if (PaperLib.isPaper()) { + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null, + false // last false is to not bother with x-ray + ); + } else { + // deprecated on paper - deprecation suppressed + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null); + } + nearbyPlayers(nmsWorld, pos).forEach(p -> p.connection.send(packet)); + } finally { + NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); + } + }); + } } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { @@ -395,15 +426,14 @@ private static List nearbyPlayers(ServerLevel serverLevel, ChunkPo } /* - NMS conversion + * NMS conversion */ public static LevelChunkSection newChunkSection( final int layer, final char[] blocks, CachedBukkitAdapter adapter, RegistryAccess registryAccess, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { return newChunkSection(layer, null, blocks, adapter, registryAccess, biomes); } @@ -413,8 +443,7 @@ public static LevelChunkSection newChunkSection( char[] set, CachedBukkitAdapter adapter, RegistryAccess registryAccess, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (set == null) { return newChunkSection(registryAccess, biomes); } @@ -465,14 +494,15 @@ public static LevelChunkSection newChunkSection( // Create palette with data var strategy = Strategy.createForBlockStates(Block.BLOCK_STATE_REGISTRY); - var packedData = new PalettedContainerRO.PackedData<>(palette, Optional.of(LongStream.of(bits)), bitsPerEntry); + var packedData = new PalettedContainerRO.PackedData<>(palette, Optional.of(LongStream.of(bits)), + bitsPerEntry); DataResult> result; if (PaperLib.isPaper()) { result = PalettedContainer.unpack(strategy, packedData, Blocks.AIR.defaultBlockState(), null); } else { - //noinspection unchecked - result = (DataResult>) - palettedContainerUnpackSpigot.invokeExact(strategy, packedData); + // noinspection unchecked + result = (DataResult>) palettedContainerUnpackSpigot + .invokeExact(strategy, packedData); } if (biomes == null) { biomes = PalettedContainerFactory.create(registryAccess).createForBiomes(); @@ -491,8 +521,7 @@ public static LevelChunkSection newChunkSection( @SuppressWarnings("deprecation") // Only deprecated in paper private static LevelChunkSection newChunkSection( RegistryAccess registryAccess, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { PalettedContainerFactory factory = PalettedContainerFactory.create(registryAccess); if (biomes == null) { return new LevelChunkSection(factory); @@ -509,17 +538,18 @@ public static void setBiomesToChunkSection(LevelChunkSection section, PalettedCo } /** - * Create a new {@link PalettedContainer}. Should only be used if no biome container existed beforehand. + * Create a new {@link PalettedContainer}. Should only be used if no + * biome container existed beforehand. */ public static PalettedContainer> getBiomePalettedContainer( BiomeType[] biomes, - IdMap> biomeRegistry - ) { + IdMap> biomeRegistry) { if (biomes == null) { return null; } BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - // Don't stream this as typically will see 1-4 biomes; stream overhead is large for the small length + // Don't stream this as typically will see 1-4 biomes; stream overhead is large + // for the small length final List> palette = new ArrayList<>(); for (BiomeType biomeType : new LinkedList<>(Set.of(biomes))) { if (biomeType == null) { @@ -540,21 +570,19 @@ public static PalettedContainer> getBiomePalettedContainer( var strategy = Strategy.createForBiomes(biomeRegistry); var packedData = new PalettedContainerRO.PackedData<>( - palette, Optional.of(LongStream.of(new long[arrayLength])), bitsPerEntry - ); + palette, Optional.of(LongStream.of(new long[arrayLength])), bitsPerEntry); DataResult>> result; if (PaperLib.isPaper()) { result = PalettedContainer.unpack( strategy, packedData, biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - null - ); + null); } else { try { - //noinspection unchecked - result = (DataResult>>) - palettedContainerUnpackSpigot.invokeExact(strategy, packedData); + // noinspection unchecked + result = (DataResult>>) palettedContainerUnpackSpigot + .invokeExact(strategy, packedData); } catch (Throwable e) { throw new RuntimeException("Failed to create biome palette for Spigot", e); } @@ -620,11 +648,13 @@ static List getEntities(LevelChunk chunk) { if (PaperLib.isPaper()) { return Optional.ofNullable(chunk.level .moonrise$getEntityLookup() - .getChunk(chunk.locX, chunk.locZ)).map(ChunkEntitySlices::getAllEntities).orElse(Collections.emptyList()); + .getChunk(chunk.locX, chunk.locZ)).map(ChunkEntitySlices::getAllEntities) + .orElse(Collections.emptyList()); } try { - //noinspection unchecked - return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos()); + // noinspection unchecked + return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))) + .getEntities(chunk.getPos()); } catch (IllegalAccessException e) { throw new RuntimeException("Failed to lookup entities [PAPER=false]", e); } diff --git a/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/regen/PaperweightRegen.java index 549a021400..2edfe5a23d 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/regen/PaperweightRegen.java @@ -1,6 +1,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_11.regen; import com.fastasyncworldedit.bukkit.adapter.Regenerator; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; @@ -20,6 +21,7 @@ import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.ProgressListener; +import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelSettings; import net.minecraft.world.level.biome.Biome; @@ -39,6 +41,8 @@ import java.nio.file.Path; import java.util.Map; import java.util.OptionalLong; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.function.BooleanSupplier; import java.util.function.Supplier; @@ -50,14 +54,13 @@ public class PaperweightRegen extends Regenerator { private static final Field paperConfigField; private static final Field generatorSettingBaseSupplierField; - static { try { serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField.setAccessible(true); Field tmpPaperConfigField; - try { //only present on paper + try { // only present on paper tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField.setAccessible(true); } catch (Exception e) { @@ -73,7 +76,7 @@ public class PaperweightRegen extends Regenerator { } } - //runtime + // runtime private ServerLevel originalServerWorld; private ServerLevel freshWorld; private LevelStorageSource.LevelStorageAccess session; @@ -84,8 +87,7 @@ public PaperweightRegen( World originalBukkitWorld, Region region, Extent target, - RegenOptions options - ) { + RegenOptions options) { super(originalBukkitWorld, region, target, options); } @@ -107,10 +109,10 @@ protected boolean prepare() { @Override protected boolean initNewWorld() throws Exception { - //world folder + // world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); - //prepare for world init (see upstream implementation for reference) + // prepare for world init (see upstream implementation for reference) World.Environment environment = originalBukkitWorld.getEnvironment(); org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator(); LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir); @@ -130,21 +132,19 @@ protected boolean initNewWorld() throws Exception { originalWorldData.settings.difficulty(), originalWorldData.settings.allowCommands(), originalWorldData.settings.gameRules(), - originalWorldData.settings.getDataConfiguration() - ); + originalWorldData.settings.getDataConfiguration()); - PrimaryLevelData.SpecialWorldProperty specialWorldProperty = - originalWorldData.isFlatWorld() - ? PrimaryLevelData.SpecialWorldProperty.FLAT - : originalWorldData.isDebugWorld() - ? PrimaryLevelData.SpecialWorldProperty.DEBUG - : PrimaryLevelData.SpecialWorldProperty.NONE; - PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable()); + PrimaryLevelData.SpecialWorldProperty specialWorldProperty = originalWorldData.isFlatWorld() + ? PrimaryLevelData.SpecialWorldProperty.FLAT + : originalWorldData.isDebugWorld() + ? PrimaryLevelData.SpecialWorldProperty.DEBUG + : PrimaryLevelData.SpecialWorldProperty.NONE; + PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, + Lifecycle.stable()); BiomeProvider biomeProvider = getBiomeProvider(); - - //init world + // init world freshWorld = Fawe.instance().getQueueHandler().sync((Supplier) () -> new ServerLevel( server, server.executor, @@ -153,8 +153,7 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.dimension(), new LevelStem( originalServerWorld.dimensionTypeRegistration(), - originalServerWorld.getChunkSource().getGenerator() - ), + originalServerWorld.getChunkSource().getGenerator()), originalServerWorld.isDebug(), seed, ImmutableList.of(), @@ -162,13 +161,14 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.getRandomSequences(), environment, generator, - biomeProvider - ) { + biomeProvider) { - private final Holder singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess() - .lookupOrThrow(BIOME).asHolderIdMap().byIdOrThrow( - WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) - ) : null; + private final Holder singleBiome = options.hasBiomeType() + ? DedicatedServer.getServer().registryAccess() + .lookupOrThrow(BIOME).asHolderIdMap().byIdOrThrow( + WorldEditPlugin.getInstance().getBukkitImplAdapter() + .getInternalBiomeId(options.getBiomeType())) + : null; @Override public @Nonnull Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { @@ -182,8 +182,7 @@ protected boolean initNewWorld() throws Exception { public void save( final ProgressListener progressListener, final boolean flush, - final boolean savingDisabled - ) { + final boolean savingDisabled) { // noop, spigot } @@ -192,20 +191,58 @@ public void save( final ProgressListener progressListener, final boolean flush, final boolean savingDisabled, - final boolean close - ) { + final boolean close) { // noop, paper } }).get(); freshWorld.noSave = true; removeWorldFromWorldsMap(); - newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name + newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); // rename to original world name if (paperConfigField != null) { paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); } + + if (FoliaLibHolder.isFolia()) { + return initWorldForFolia(newWorldData); + } + return true; } + private boolean initWorldForFolia(PrimaryLevelData worldData) throws ExecutionException, InterruptedException { + MinecraftServer console = ((CraftServer) Bukkit.getServer()).getServer(); + + ChunkPos spawnChunk = new ChunkPos( + freshWorld.getChunkSource().randomState().sampler().findSpawnPosition()); + + setRandomSpawnSelection(spawnChunk); + + CompletableFuture initFuture = new CompletableFuture<>(); + + FoliaLibHolder.getScheduler().runAtLocation( + freshWorld.getWorld().getChunkAt(spawnChunk.x, spawnChunk.z).getBlock(0, 0, 0).getLocation(), + task -> { + try { + console.initWorld(freshWorld, worldData, worldData.worldGenOptions()); + initFuture.complete(true); + } catch (Exception e) { + initFuture.completeExceptionally(e); + } + }); + + return initFuture.get(); + } + + private void setRandomSpawnSelection(ChunkPos spawnChunk) { + try { + Field randomSpawnField = ServerLevel.class.getDeclaredField("randomSpawnSelection"); + randomSpawnField.setAccessible(true); + randomSpawnField.set(freshWorld, spawnChunk); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to set randomSpawnSelection for Folia world initialization", e); + } + } + @Override protected void cleanup() { try { @@ -213,7 +250,7 @@ protected void cleanup() { } catch (Exception ignored) { } - //shutdown chunk provider + // shutdown chunk provider try { Fawe.instance().getQueueHandler().sync(() -> { try { @@ -226,13 +263,13 @@ protected void cleanup() { } catch (Exception ignored) { } - //remove world from server + // remove world from server try { Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap); } catch (Exception ignored) { } - //delete directory + // delete directory try { SafeFiles.tryHardToDeleteDir(tempDir); } catch (Exception ignored) { @@ -244,7 +281,12 @@ protected IChunkCache initSourceQueueCache() { return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } - //util + @Override + protected World getFreshWorld() { + return freshWorld != null ? freshWorld.getWorld() : null; + } + + // util @SuppressWarnings("unchecked") private void removeWorldFromWorldsMap() { try { diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweAdapter.java index 82cccce624..07d854793a 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweAdapter.java @@ -10,6 +10,7 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; @@ -20,6 +21,7 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_4.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4.regen.PaperweightRegen; @@ -129,6 +131,7 @@ import java.util.Objects; import java.util.OptionalInt; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -141,12 +144,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter private static final Logger LOGGER = LogManagerCompat.getLogger(); private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE; private static final Codec COMPONENTS_CODEC = DataComponentPatch.CODEC.optionalFieldOf( - "components", DataComponentPatch.EMPTY - ).codec(); + "components", DataComponentPatch.EMPTY).codec(); static { try { - CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave"); + CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class + .getDeclaredMethod("wasAccessibleSinceLastSave"); } catch (NoSuchMethodException ignored) { // may not be present in newer paper versions } } @@ -159,29 +162,31 @@ public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodExcepti public Function blockEntityToCompoundTag() { return blockEntity -> FaweCompoundTag.of( - () -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId(DedicatedServer.getServer().registryAccess())) - ); + () -> (LinCompoundTag) toNativeLin( + blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()))); } public static Property adaptProperty(net.minecraft.world.level.block.state.properties.Property property) { return switch (property) { case net.minecraft.world.level.block.state.properties.BooleanProperty booleanProperty -> - new BooleanProperty(booleanProperty.getName(), ImmutableList.copyOf(booleanProperty.getPossibleValues())); + new BooleanProperty(booleanProperty.getName(), + ImmutableList.copyOf(booleanProperty.getPossibleValues())); case net.minecraft.world.level.block.state.properties.IntegerProperty integerProperty -> - new IntegerProperty(integerProperty.getName(), ImmutableList.copyOf(integerProperty.getPossibleValues())); + new IntegerProperty(integerProperty.getName(), + ImmutableList.copyOf(integerProperty.getPossibleValues())); case net.minecraft.world.level.block.state.properties.EnumProperty enumProperty -> { if (enumProperty.getValueClass() == net.minecraft.core.Direction.class) { yield new DirectionalProperty(enumProperty.getName(), enumProperty.getPossibleValues().stream() .map(StringRepresentable::getSerializedName) .map(s -> s.toUpperCase(Locale.ROOT)) .map(Direction::valueOf) - .toList() - ); + .toList()); } yield new EnumProperty(enumProperty.getName(), enumProperty.getPossibleValues().stream() .map(StringRepresentable::getSerializedName).collect(Collectors.toCollection(ArrayList::new))); } - default -> throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + property.getClass().getSimpleName()); + default -> throw new IllegalArgumentException( + "FastAsyncWorldEdit needs an update to support " + property.getClass().getSimpleName()); }; } @@ -262,7 +267,8 @@ public BlockMaterial getMaterial(BlockType blockType) { @Override public synchronized BlockMaterial getMaterial(BlockState state) { - net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState(); + net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit + .createBlockData(state.getAsString())).getState(); return new PaperweightBlockMaterial(blockState.getBlock(), blockState); } @@ -325,8 +331,7 @@ public BaseBlock getFullBlock(final Location location) { SideEffect.HISTORY, SideEffect.HEIGHTMAPS, SideEffect.LIGHTING, - SideEffect.NEIGHBORS - ); + SideEffect.NEIGHBORS); @Override public Set getSupportedSideEffects() { @@ -352,7 +357,7 @@ public BaseEntity getEntity(org.bukkit.entity.Entity entity) { if (!readEntityIntoTag(mcEntity, minecraftTag)) { return null; } - //add Id for AbstractChangeSet to work + // add Id for AbstractChangeSet to work final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag); final Map> tags = NbtUtils.getLinCompoundTagValues(tag); tags.put("Id", LinStringTag.of(id)); @@ -409,8 +414,7 @@ public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockSt return (char) ibdToOrdinal[id]; } catch (ArrayIndexOutOfBoundsException e1) { LOGGER.error("Attempted to convert {} with ID {} to char. ibdToOrdinal length: {}. Defaulting to air!", - blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1 - ); + blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1); return BlockTypesCache.ReservedIDs.AIR; } } @@ -483,18 +487,22 @@ public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockSt @Override public void sendFakeChunk(World world, Player player, ChunkPacket chunkPacket) { ServerLevel nmsWorld = getServerLevel(world); - ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); + ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), + chunkPacket.getChunkZ()); if (map != null && wasAccessibleSinceLastSave(map)) { boolean flag = false; // PlayerChunk.d players = map.players; - Stream stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag) - */ Stream.empty(); + Stream stream = /* + * players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), + * flag) + */ Stream.empty(); ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle(); stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer) .forEach(entityPlayer -> { synchronized (chunkPacket) { - ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket.getNativePacket(); + ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket + .getNativePacket(); if (nmsPacket == null) { nmsPacket = mapUtil.create(this, chunkPacket); chunkPacket.setNativePacket(nmsPacket); @@ -521,8 +529,7 @@ public boolean canPlaceAt(World world, BlockVector3 blockVector3, BlockState blo net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); return blockState1.hasPostProcess( getServerLevel(world), - new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z()) - ); + new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z())); } @Override @@ -530,10 +537,8 @@ public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) { final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess(); ItemStack stack = new ItemStack( registryAccess.lookupOrThrow(Registries.ITEM).getValueOrThrow(ResourceKey.create( - Registries.ITEM, ResourceLocation.parse(baseItemStack.getType().id()) - )), - baseItemStack.getAmount() - ); + Registries.ITEM, ResourceLocation.parse(baseItemStack.getType().id()))), + baseItemStack.getAmount()); final CompoundTag nbt = (CompoundTag) fromNativeLin(baseItemStack.getNbt()); if (nbt != null) { final DataComponentPatch patch = COMPONENTS_CODEC @@ -567,8 +572,21 @@ protected ServerLevel getServerLevel(final World world) { return ((CraftWorld) world).getHandle(); } + private T syncRegion(World world, BlockVector3 pt, java.util.function.Supplier supplier) { + if (FoliaLibHolder.isFolia()) { + Location location = new Location(world, pt.x(), pt.y(), pt.z()); + CompletableFuture future = new CompletableFuture<>(); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> future.complete(supplier.get())); + return future.join(); + } + return TaskManager.taskManager().sync(supplier); + } + @Override - public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, BlockVector3 pt) { + public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, + BlockVector3 pt) { ServerLevel serverLevel = getServerLevel(world); ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); @@ -578,15 +596,14 @@ public boolean generateFeature(ConfiguredFeatureType feature, World world, EditS .getValue(ResourceLocation.tryParse(feature.id())); FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { if (!configuredFeature.place( populator, generator, serverLevel.random, - new BlockPos(pt.x(), pt.y(), pt.z()) - )) { + new BlockPos(pt.x(), pt.y(), pt.z()))) { return null; } List placedBlocks = new ArrayList<>(populator.getList()); @@ -614,7 +631,7 @@ public boolean generateStructure(StructureType type, World world, EditSession ed ChunkPos chunkPos = new ChunkPos(new BlockPos(pt.x(), pt.y(), pt.z())); FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { StructureStart structureStart = structure.generate( @@ -629,20 +646,17 @@ public boolean generateStructure(StructureType type, World world, EditSession ed chunkPos, 0, populator, - biome -> true - ); + biome -> true); if (!structureStart.isValid()) { return null; } else { BoundingBox boundingBox = structureStart.getBoundingBox(); ChunkPos min = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.minX()), - SectionPos.blockToSectionCoord(boundingBox.minZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.minZ())); ChunkPos max = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.maxX()), - SectionPos.blockToSectionCoord(boundingBox.maxZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.maxZ())); ChunkPos.rangeClosed(min, max).forEach((chunkPosx) -> structureStart.placeInChunk( populator, serverLevel.structureManager(), @@ -654,10 +668,8 @@ public boolean generateStructure(StructureType type, World world, EditSession ed chunkPosx.getMinBlockZ(), chunkPosx.getMaxBlockX(), serverLevel.getMaxY(), - chunkPosx.getMaxBlockZ() - ), - chunkPosx - )); + chunkPosx.getMaxBlockZ()), + chunkPosx)); List placedBlocks = new ArrayList<>(populator.getList()); placedBlocks.addAll(serverLevel.capturedBlockStates.values()); return placedBlocks; @@ -673,8 +685,7 @@ public boolean generateStructure(StructureType type, World world, EditSession ed private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, - final List placed - ) { + final List placed) { if (placed == null || placed.isEmpty()) { return false; } @@ -684,7 +695,8 @@ private boolean placeFeatureIntoSession( continue; } BlockPos pos = craftBlockState.getPosition(); - editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), BukkitAdapter.adapt(craftBlockState.getBlockData())); + editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), + BukkitAdapter.adapt(craftBlockState.getBlockData())); BlockEntity blockEntity = populator.getBlockEntity(pos); if (blockEntity != null) { CompoundTag tag = blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()); @@ -700,7 +712,8 @@ public void setupFeatures() { // All these features should be the "face" selected Set face_features = Arrays - .stream(new Class[]{AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, VegetationFeatures.class}) + .stream(new Class[] { AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, + VegetationFeatures.class }) .flatMap(c -> Arrays.stream(c.getFields())) .filter(f -> { int modifiers = f.getModifiers(); @@ -754,16 +767,15 @@ public void setupFeatures() { public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess(); final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); - // We should be fine to perform this later as we're using a deep-copied itemstack (above) + // We should be fine to perform this later as we're using a deep-copied + // itemstack (above) final Supplier tag = () -> COMPONENTS_CODEC.encodeStart( registryAccess.createSerializationContext(NbtOps.INSTANCE), - nmsStack.getComponentsPatch() - ).getOrThrow(); + nmsStack.getComponentsPatch()).getOrThrow(); return new BaseItemStack( BukkitAdapter.asItemType(itemStack.getType()), LazyReference.from(() -> (LinCompoundTag) toNativeLin(tag.get())), - itemStack.getAmount() - ); + itemStack.getAmount()); } @Override diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweWorldNativeAccess.java index 60a1d5c13a..73dc792077 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweWorldNativeAccess.java @@ -22,6 +22,7 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.redstone.ExperimentalRedstoneUtils; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.event.block.BlockPhysicsEvent; @@ -60,7 +61,7 @@ public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAd this.level = level; // Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging. // - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway. - this.lastTick = new AtomicInteger(MinecraftServer.currentTick); + this.lastTick = new AtomicInteger(getCurrentTick()); } private Level getLevel() { @@ -96,7 +97,7 @@ public synchronized net.minecraft.world.level.block.state.BlockState setBlockSta LevelChunk levelChunk, BlockPos blockPos, net.minecraft.world.level.block.state.BlockState blockState ) { - int currentTick = MinecraftServer.currentTick; + int currentTick = getCurrentTick(); if (Fawe.isMainThread()) { return levelChunk.setBlockState(blockPos, blockState, this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE) @@ -291,4 +292,17 @@ private record CachedChange( } + + private int getCurrentTick() { + try { + return MinecraftServer.currentTick; + } catch (NoSuchFieldError e) { + try { + return Bukkit.getCurrentTick(); + } catch (Exception ex) { + return 0; + } + } + } + } diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java index 4d173a9f05..f7a268e994 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java @@ -11,6 +11,7 @@ import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; @@ -58,6 +59,8 @@ import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.lighting.LevelLightEngine; import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.World; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.CraftBlock; @@ -95,7 +98,8 @@ public class PaperweightGetBlocks extends AbstractBukkitGetBlocks posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); + private static final Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), + v.getZ()); public static final Function NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin .getInstance() .getBukkitImplAdapter()).blockEntityToCompoundTag(); @@ -168,8 +172,9 @@ public BiomeType getBiomeType(int x, int y, int z) { @Override public void removeSectionLighting(int layer, boolean sky) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK) + .getDataLayerData( + sectionPos); if (dataLayer != null) { lightUpdate = true; synchronized (dataLayer) { @@ -196,9 +201,8 @@ public void removeSectionLighting(int layer, boolean sky) { @Override 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))); + BlockEntity blockEntity = getChunk() + .getBlockEntity(new BlockPos((x & 15) + (chunkX << 4), y, (z & 15) + (chunkZ << 4))); if (blockEntity == null) { return null; } @@ -221,19 +225,19 @@ public int getSkyLight(int x, int y, int z) { int alayer = layer - getMinSectionPosition(); if (skyLight[alayer] == null) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = - serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getDataLayerData(sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY) + .getDataLayerData(sectionPos); // If the server hasn't generated the section's NibbleArray yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( LightLayer.BLOCK, sectionPos, - dataLayer - ); + dataLayer); } skyLight[alayer] = dataLayer; } @@ -255,12 +259,13 @@ public int getEmittedLight(int x, int y, int z) { // If the server hasn't generated the section's DataLayer yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); - ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, sectionPos, - dataLayer - ); + ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, + sectionPos, + dataLayer); } blockLight[alayer] = dataLayer; } @@ -333,13 +338,12 @@ protected > T internalCall( Runnable finalizer, int copyKey, LevelChunk nmsChunk, - ServerLevel nmsWorld - ) throws Exception { + ServerLevel nmsWorld) throws Exception { PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null; if (createCopy) { if (copies.containsKey(copyKey)) { throw new IllegalStateException("Copy key already used."); - } + } copies.put(copyKey, copy); } // Remove existing tiles. Create a copy so that we can remove blocks @@ -364,7 +368,18 @@ protected > T internalCall( beacons = new ArrayList<>(); } beacons.add(tile); - PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + if (FoliaLibHolder.isFolia()) { + Location location = new Location( + nmsWorld.getWorld(), + tile.getBlockPos().getX(), + tile.getBlockPos().getY(), + tile.getBlockPos().getZ()); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk)); + } else { + PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + } continue; } nmsChunk.removeBlockEntity(tile.getBlockPos()); @@ -401,33 +416,32 @@ protected > T internalCall( } if (existingSection == null) { - PalettedContainer> biomeData = PaperweightPlatformAdapter.getBiomePalettedContainer( - biomes[setSectionIndex], - biomeHolderIdMap - ); + PalettedContainer> biomeData = PaperweightPlatformAdapter + .getBiomePalettedContainer( + biomes[setSectionIndex], + biomeHolderIdMap); LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, new char[4096], adapter, biomeRegistry, - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { - updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex); + getSectionIndex)) { + updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], + getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; if (existingSection == null) { - LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, + chunkZ, + getSectionIndex); continue; } } @@ -435,8 +449,7 @@ protected > T internalCall( PalettedContainer> paletteBiomes = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); if (paletteBiomes != null) { PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes); } @@ -448,13 +461,16 @@ protected > T internalCall( bitMask |= 1 << getSectionIndex; - // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to - // this chunk GET when #updateGet is called. Future dords, please listen this time. + // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in + // order to write changes to + // this chunk GET when #updateGet is called. Future dords, please listen this + // time. char[] tmp = set.load(layerNo); char[] setArr = new char[tmp.length]; System.arraycopy(tmp, 0, setArr, 0, tmp.length); - // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was + // synchronise on internal section to avoid circular locking with a continuing + // edit if the chunk was // submitted to keep loaded internal chunks to queue target size. synchronized (super.sectionLocks[getSectionIndex]) { @@ -479,37 +495,36 @@ protected > T internalCall( PalettedContainer> biomeData = biomes == null ? new PalettedContainer<>( biomeHolderIdMap, biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap); + PalettedContainer.Strategy.SECTION_BIOMES) + : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], + biomeHolderIdMap); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, setArr, adapter, biomeRegistry, - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { + getSectionIndex)) { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; if (existingSection == null) { LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); continue; } } } - //ensure that the server doesn't try to tick the chunksection while we're editing it. (Again) + // ensure that the server doesn't try to tick the chunksection while we're + // editing it. (Again) PaperweightPlatformAdapter.clearCounts(existingSection); DelegateSemaphore lock = PaperweightPlatformAdapter.applyLock(existingSection); @@ -528,11 +543,12 @@ protected > T internalCall( this.reset(); } else if (!Arrays.equals( update(getSectionIndex, new char[4096], true), - load(layerNo) - )) { + load(layerNo))) { this.reset(layerNo); - /*} else if (lock.isModified()) { - this.reset(layerNo);*/ + /* + * } else if (lock.isModified()) { + * this.reset(layerNo); + */ } } finally { sectionLock.writeLock().unlock(); @@ -541,8 +557,7 @@ protected > T internalCall( PalettedContainer> biomeData = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, @@ -550,19 +565,17 @@ protected > T internalCall( setArr, adapter, biomeRegistry, - biomeData != null ? biomeData : (PalettedContainer>) existingSection.getBiomes() - ); + biomeData != null ? biomeData + : (PalettedContainer>) existingSection.getBiomes()); if (!PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, existingSection, newSection, - getSectionIndex - )) { + getSectionIndex)) { LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); } else { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); } @@ -577,13 +590,11 @@ protected > T internalCall( PaperweightGetBlocks.this.setLightingToGet( set.getLight(), set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); + set.getMaxSectionPosition()); PaperweightGetBlocks.this.setSkyLightingToGet( set.getSkyLight(), set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); + set.getMaxSectionPosition()); List syncTasks = new ArrayList<>(); @@ -596,7 +607,8 @@ protected > T internalCall( final List finalBeacons = beacons; syncTasks.add(() -> { for (BlockEntity beacon : finalBeacons) { - BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), SoundEvents.BEACON_DEACTIVATE); + BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), + SoundEvents.BEACON_DEACTIVATE); new BeaconDeactivatedEvent(CraftBlock.at(beacon.getLevel(), beacon.getBlockPos())).callEvent(); } }); @@ -665,17 +677,33 @@ protected > T internalCall( entity.load(tag); entity.absMoveTo(x, y, z, yaw, pitch); entity.setUUID(NbtUtils.uuid(nativeTag)); - if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { - LOGGER.warn( - "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", - id, - nmsWorld.getWorld().getName(), - x, - y, - z - ); - // Unsuccessful create should not be saved to history - iterator.remove(); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } + }); + } else { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } } } } @@ -695,18 +723,39 @@ protected > T internalCall( final int z = blockHash.z() + bz; final BlockPos pos = new BlockPos(x, y, z); - synchronized (nmsWorld) { - BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); - if (tileEntity == null || tileEntity.isRemoved()) { - nmsWorld.removeBlockEntity(pos); - tileEntity = nmsWorld.getBlockEntity(pos); - } - if (tileEntity != null) { - 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)); - tileEntity.loadWithComponents(tag, DedicatedServer.getServer().registryAccess()); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + tileEntity.loadWithComponents(tag, + DedicatedServer.getServer().registryAccess()); + } + } + }); + } else { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + tileEntity.loadWithComponents(tag, DedicatedServer.getServer().registryAccess()); + } } } } @@ -728,7 +777,8 @@ protected > T internalCall( // send to player if (!set .getSideEffectSet() - .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) { + .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING + || finalMask == 0 && biomes != null) { this.send(); } if (finalizer != null) { @@ -745,8 +795,7 @@ private void updateGet( LevelChunkSection[] chunkSections, LevelChunkSection section, char[] arr, - int layer - ) { + int layer) { try { sectionLock.writeLock().lock(); if (this.getChunk() != nmsChunk) { @@ -760,8 +809,9 @@ private void updateGet( System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); } if (this.sections[layer] != section) { - // Not sure why it's funky, but it's what I did in commit fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords - this.sections[layer] = new LevelChunkSection[]{section}.clone()[0]; + // Not sure why it's funky, but it's what I did in commit + // fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords + this.sections[layer] = new LevelChunkSection[] { section }.clone()[0]; } } finally { sectionLock.writeLock().unlock(); @@ -777,14 +827,19 @@ public void send() { } /** - * Update a given (nullable) data array to the current data stored in the server's chunk, associated with this - * {@link PaperweightPlatformAdapter} instance. Not synchronised to the {@link PaperweightPlatformAdapter} instance as synchronisation - * is handled where necessary in the method, and should otherwise be handled correctly by this method's caller. + * Update a given (nullable) data array to the current data stored in the + * server's chunk, associated with this + * {@link PaperweightPlatformAdapter} instance. Not synchronised to the + * {@link PaperweightPlatformAdapter} instance as synchronisation + * is handled where necessary in the method, and should otherwise be handled + * correctly by this method's caller. * - * @param layer layer index (0 may denote a negative layer in the world, e.g. at y=-32) + * @param layer layer index (0 may denote a negative layer in the world, + * e.g. at y=-32) * @param data array to be updated/filled with data or null * @param aggressive if the cached section array should be re-acquired. - * @return the given array to be filled with data, or a new array if null is given. + * @return the given array to be filled with data, or a new array if null is + * given. */ @Override @SuppressWarnings("unchecked") @@ -814,7 +869,8 @@ public char[] update(int layer, char[] data, boolean aggressive) { return data; } - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get(dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get(dataObject); final int bitsPerEntry = bits.getBits(); final long[] blockStates = bits.getRaw(); @@ -894,10 +950,10 @@ public LevelChunk getChunk() { } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e); throw new FaweException( - TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), + TextComponent + .of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), FaweException.Type.OTHER, - false - ); + false); } } } @@ -905,14 +961,16 @@ public LevelChunk getChunk() { return levelChunk; } - private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, int maxSectionPosition) { + private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, + int maxSectionPosition) { for (int Y = 0; Y <= maxSectionPosition - minSectionPosition; Y++) { if (light[Y] == null) { continue; } SectionPos sectionPos = SectionPos.of(levelChunk.getPos(), Y + minSectionPosition); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer) + .getDataLayerData( + sectionPos); if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; Arrays.fill(LAYER_COUNT, lightLayer == LightLayer.SKY ? (byte) 15 : (byte) 0); @@ -920,8 +978,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( lightLayer, sectionPos, - dataLayer - ); + dataLayer); } synchronized (dataLayer) { for (int x = 0; x < 16; x++) { @@ -941,8 +998,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti private PalettedContainer> setBiomesToPalettedContainer( final BiomeType[][] biomes, final int sectionIndex, - final PalettedContainerRO> data - ) { + final PalettedContainerRO> data) { BiomeType[] sectionBiomes; if (biomes == null || (sectionBiomes = biomes[sectionIndex]) == null) { return null; @@ -959,8 +1015,7 @@ private PalettedContainer> setBiomesToPalettedContainer( x, y, z, - biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType)) - ); + biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType))); } } } @@ -1016,8 +1071,9 @@ public boolean trim(boolean aggressive) { final PalettedContainer blocksExisting = existing.getStates(); final Object dataObject = PaperweightPlatformAdapter.fieldData.get(blocksExisting); - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get( - dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get( + dataObject); int paletteSize; if (palette instanceof LinearPalette || palette instanceof HashMapPalette) { @@ -1027,7 +1083,8 @@ public boolean trim(boolean aggressive) { continue; } if (paletteSize == 1) { - //If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks. + // If the cached palette size is 1 then no blocks can have been changed i.e. do + // not need to update these chunks. continue; } super.trim(false, i); diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightPlatformAdapter.java index cdd4f7d7b7..848120d2aa 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightPlatformAdapter.java @@ -9,6 +9,7 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.bukkit.WorldEditPlugin; @@ -20,6 +21,7 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import io.papermc.lib.PaperLib; +import io.papermc.paper.util.MCUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.IdMap; @@ -124,13 +126,16 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c")); fieldPalette.setAccessible(true); - fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "g")); + fieldTickingFluidCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingFluidCount", "g")); fieldTickingFluidCount.setAccessible(true); - fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); + fieldTickingBlockCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); fieldTickingBlockCount.setAccessible(true); Field tmpFieldBiomes; try { - // Seems it's sometimes biomes and sometimes "i". Idk this is just easier than having to try to deal with it + // Seems it's sometimes biomes and sometimes "i". Idk this is just easier than + // having to try to deal with it tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("biomes"); // apparently unobf } catch (NoSuchFieldException ignored) { tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("i"); // apparently obf @@ -141,18 +146,19 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod( Refraction.pickName( "getVisibleChunkIfPresent", - "b" - ), long.class - ); + "b"), + long.class); getVisibleChunkIfPresent.setAccessible(true); methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent); if (!PaperLib.isPaper()) { - fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f")); + fieldThreadingDetector = PalettedContainer.class + .getDeclaredField(Refraction.pickName("threadingDetector", "f")); fieldThreadingDetector.setAccessible(true); fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); fieldLock.setAccessible(true); - SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "O")); + SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class + .getDeclaredField(Refraction.pickName("entityManager", "O")); SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true); } else { // in paper, the used methods are synchronized properly @@ -163,17 +169,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( Refraction.pickName("removeGameEventListener", "a"), BlockEntity.class, - ServerLevel.class - ); + ServerLevel.class); removeGameEventListener.setAccessible(true); methodRemoveGameEventListener = lookup.unreflect(removeGameEventListener); Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod( Refraction.pickName( "removeBlockEntityTicker", - "k" - ), BlockPos.class - ); + "k"), + BlockPos.class); removeBlockEntityTicker.setAccessible(true); methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker); @@ -182,8 +186,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method palettedContaienrGet = PalettedContainer.class.getDeclaredMethod( Refraction.pickName("get", "a"), - int.class - ); + int.class); palettedContaienrGet.setAccessible(true); PALETTED_CONTAINER_GET = lookup.unreflect(palettedContaienrGet); } catch (RuntimeException | Error e) { @@ -199,14 +202,13 @@ static boolean setSectionAtomic( LevelChunkSection[] sections, LevelChunkSection expected, LevelChunkSection value, - int layer - ) { + int layer) { return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer); } // There is no point in having a functional semaphore for paper servers. - private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = - ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null)); + private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = ThreadLocal + .withInitial(() -> new DelegateSemaphore(1, null)); static DelegateSemaphore applyLock(LevelChunkSection section) { if (PaperLib.isPaper()) { @@ -261,11 +263,11 @@ public static CompletableFuture ensureLoaded(ServerLevel serverLevel "Unexpected error when getting completed future at chunk {},{}. Returning to default.", chunkX, chunkZ, - e - ); + e); } } - return CompletableFuture.supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); + return CompletableFuture + .supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); } private static LevelChunk toLevelChunk(Chunk chunk) { @@ -302,6 +304,14 @@ private static LevelChunk toLevelChunk(Chunk chunk) { } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { + if (FoliaLibHolder.isFolia()) { + try { + serverLevel.getChunkSource().addRegionTicket(ChunkHolderManager.UNLOAD_COOLDOWN, + new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE); + } catch (Exception ignored) { + } + return; + } // Ensure chunk is definitely loaded before applying a ticket io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel .getChunkSource() @@ -338,7 +348,7 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int if (lockHolder.chunkLock == null) { return; } - MinecraftServer.getServer().execute(() -> { + if (FoliaLibHolder.isFolia()) { try { ChunkPos pos = levelChunk.getPos(); ClientboundLevelChunkWithLightPacket packet; @@ -356,14 +366,39 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int levelChunk, nmsWorld.getLightEngine(), null, - null - ); + null); } nearbyPlayers(nmsWorld, pos).forEach(p -> p.connection.send(packet)); } finally { NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); } - }); + } else { + MinecraftServer.getServer().execute(() -> { + try { + ChunkPos pos = levelChunk.getPos(); + ClientboundLevelChunkWithLightPacket packet; + if (PaperLib.isPaper()) { + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null, + false // last false is to not bother with x-ray + ); + } else { + // deprecated on paper - deprecation suppressed + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null); + } + nearbyPlayers(nmsWorld, pos).forEach(p -> p.connection.send(packet)); + } finally { + NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); + } + }); + } } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { @@ -371,15 +406,14 @@ private static List nearbyPlayers(ServerLevel serverLevel, ChunkPo } /* - NMS conversion + * NMS conversion */ public static LevelChunkSection newChunkSection( final int layer, final char[] blocks, CachedBukkitAdapter adapter, Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { return newChunkSection(layer, null, blocks, adapter, biomeRegistry, biomes); } @@ -389,8 +423,7 @@ public static LevelChunkSection newChunkSection( char[] set, CachedBukkitAdapter adapter, Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (set == null) { return newChunkSection(biomeRegistry, biomes); } @@ -446,15 +479,15 @@ public static LevelChunkSection newChunkSection( } // Create palette with data - @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot - final PalettedContainer blockStatePalettedContainer = - new PalettedContainer<>( - Block.BLOCK_STATE_REGISTRY, - PalettedContainer.Strategy.SECTION_STATES, - PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, bitsPerEntry), - nmsBits, - palette - ); + @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility + // with spigot + final PalettedContainer blockStatePalettedContainer = new PalettedContainer<>( + Block.BLOCK_STATE_REGISTRY, + PalettedContainer.Strategy.SECTION_STATES, + PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, + bitsPerEntry), + nmsBits, + palette); if (biomes == null) { IdMap> biomeHolderIdMap = biomeRegistry.asHolderIdMap(); biomes = new PalettedContainer<>( @@ -464,8 +497,7 @@ public static LevelChunkSection newChunkSection( .getBukkitImplAdapter() .getInternalBiomeId( BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ); + PalettedContainer.Strategy.SECTION_BIOMES); } return new LevelChunkSection(blockStatePalettedContainer, biomes); @@ -482,16 +514,14 @@ public static LevelChunkSection newChunkSection( @SuppressWarnings("deprecation") // Only deprecated in paper private static LevelChunkSection newChunkSection( Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (biomes == null) { return new LevelChunkSection(biomeRegistry); } PalettedContainer dataPaletteBlocks = new PalettedContainer<>( Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), - PalettedContainer.Strategy.SECTION_STATES - ); + PalettedContainer.Strategy.SECTION_STATES); return new LevelChunkSection(dataPaletteBlocks, biomes); } @@ -504,17 +534,18 @@ public static void setBiomesToChunkSection(LevelChunkSection section, PalettedCo } /** - * Create a new {@link PalettedContainer}. Should only be used if no biome container existed beforehand. + * Create a new {@link PalettedContainer}. Should only be used if no + * biome container existed beforehand. */ public static PalettedContainer> getBiomePalettedContainer( BiomeType[] biomes, - IdMap> biomeRegistry - ) { + IdMap> biomeRegistry) { if (biomes == null) { return null; } BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - // Don't stream this as typically will see 1-4 biomes; stream overhead is large for the small length + // Don't stream this as typically will see 1-4 biomes; stream overhead is large + // for the small length Map> palette = new HashMap<>(); for (BiomeType biomeType : new LinkedList<>(Arrays.asList(biomes))) { Holder biome; @@ -529,16 +560,14 @@ public static PalettedContainer> getBiomePalettedContainer( int bitsPerEntry = MathMan.log2nlz(biomeCount - 1); Object configuration = PalettedContainer.Strategy.SECTION_STATES.getConfiguration( new FakeIdMapBiome(biomeCount), - bitsPerEntry - ); + bitsPerEntry); if (bitsPerEntry > 3) { bitsPerEntry = MathMan.log2nlz(biomeRegistry.size() - 1); } PalettedContainer> biomePalettedContainer = new PalettedContainer<>( biomeRegistry, biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ); + PalettedContainer.Strategy.SECTION_BIOMES); final Palette> biomePalette; if (bitsPerEntry == 0) { @@ -573,12 +602,11 @@ public static PalettedContainer> getBiomePalettedContainer( int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes final int arrayLength = MathMan.longArrayLength(bitsPerEntryNonZero, 64); - - BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) : new SimpleBitStorage( - bitsPerEntry, - 64, - new long[arrayLength] - ); + BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) + : new SimpleBitStorage( + bitsPerEntry, + 64, + new long[arrayLength]); try { Object data = dataConstructor.newInstance(configuration, bitStorage, biomePalette); @@ -644,11 +672,13 @@ static List getEntities(LevelChunk chunk) { if (PaperLib.isPaper()) { return Optional.ofNullable(chunk.level .moonrise$getEntityLookup() - .getChunk(chunk.locX, chunk.locZ)).map(ChunkEntitySlices::getAllEntities).orElse(Collections.emptyList()); + .getChunk(chunk.locX, chunk.locZ)).map(ChunkEntitySlices::getAllEntities) + .orElse(Collections.emptyList()); } try { - //noinspection unchecked - return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos()); + // noinspection unchecked + return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))) + .getEntities(chunk.getPos()); } catch (IllegalAccessException e) { throw new RuntimeException("Failed to lookup entities [PAPER=false]", e); } diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/regen/PaperweightRegen.java index f542469d36..31736a2a05 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/regen/PaperweightRegen.java @@ -1,6 +1,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4.regen; import com.fastasyncworldedit.bukkit.adapter.Regenerator; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; @@ -41,6 +42,8 @@ import java.nio.file.Path; import java.util.Map; import java.util.OptionalLong; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.function.BooleanSupplier; import java.util.function.Supplier; @@ -52,14 +55,13 @@ public class PaperweightRegen extends Regenerator { private static final Field paperConfigField; private static final Field generatorSettingBaseSupplierField; - static { try { serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField.setAccessible(true); Field tmpPaperConfigField; - try { //only present on paper + try { // only present on paper tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField.setAccessible(true); } catch (Exception e) { @@ -75,7 +77,7 @@ public class PaperweightRegen extends Regenerator { } } - //runtime + // runtime private ServerLevel originalServerWorld; private ServerLevel freshWorld; private LevelStorageSource.LevelStorageAccess session; @@ -86,8 +88,7 @@ public PaperweightRegen( World originalBukkitWorld, Region region, Extent target, - RegenOptions options - ) { + RegenOptions options) { super(originalBukkitWorld, region, target, options); } @@ -109,10 +110,10 @@ protected boolean prepare() { @Override protected boolean initNewWorld() throws Exception { - //world folder + // world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); - //prepare for world init (see upstream implementation for reference) + // prepare for world init (see upstream implementation for reference) org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment(); org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator(); LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir); @@ -132,21 +133,19 @@ protected boolean initNewWorld() throws Exception { originalWorldData.settings.difficulty(), originalWorldData.settings.allowCommands(), originalWorldData.settings.gameRules(), - originalWorldData.settings.getDataConfiguration() - ); + originalWorldData.settings.getDataConfiguration()); - PrimaryLevelData.SpecialWorldProperty specialWorldProperty = - originalWorldData.isFlatWorld() - ? PrimaryLevelData.SpecialWorldProperty.FLAT - : originalWorldData.isDebugWorld() - ? PrimaryLevelData.SpecialWorldProperty.DEBUG - : PrimaryLevelData.SpecialWorldProperty.NONE; - PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable()); + PrimaryLevelData.SpecialWorldProperty specialWorldProperty = originalWorldData.isFlatWorld() + ? PrimaryLevelData.SpecialWorldProperty.FLAT + : originalWorldData.isDebugWorld() + ? PrimaryLevelData.SpecialWorldProperty.DEBUG + : PrimaryLevelData.SpecialWorldProperty.NONE; + PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, + Lifecycle.stable()); BiomeProvider biomeProvider = getBiomeProvider(); - - //init world + // init world freshWorld = Fawe.instance().getQueueHandler().sync((Supplier) () -> new ServerLevel( server, server.executor, @@ -155,8 +154,7 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.dimension(), new LevelStem( originalServerWorld.dimensionTypeRegistration(), - originalServerWorld.getChunkSource().getGenerator() - ), + originalServerWorld.getChunkSource().getGenerator()), new RegenNoOpWorldLoadListener(), originalServerWorld.isDebug(), seed, @@ -165,13 +163,14 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.getRandomSequences(), environment, generator, - biomeProvider - ) { + biomeProvider) { - private final Holder singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess() - .lookupOrThrow(BIOME).asHolderIdMap().byIdOrThrow( - WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) - ) : null; + private final Holder singleBiome = options.hasBiomeType() + ? DedicatedServer.getServer().registryAccess() + .lookupOrThrow(BIOME).asHolderIdMap().byIdOrThrow( + WorldEditPlugin.getInstance().getBukkitImplAdapter() + .getInternalBiomeId(options.getBiomeType())) + : null; @Override public @Nonnull Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { @@ -185,8 +184,7 @@ protected boolean initNewWorld() throws Exception { public void save( final ProgressListener progressListener, final boolean flush, - final boolean savingDisabled - ) { + final boolean savingDisabled) { // noop, spigot } @@ -195,20 +193,61 @@ public void save( final ProgressListener progressListener, final boolean flush, final boolean savingDisabled, - final boolean close - ) { + final boolean close) { // noop, paper } }).get(); freshWorld.noSave = true; removeWorldFromWorldsMap(); - newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name + newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); // rename to original world name if (paperConfigField != null) { paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); } + + if (FoliaLibHolder.isFolia()) { + return initWorldForFolia(newWorldData); + } + return true; } + private boolean initWorldForFolia(PrimaryLevelData worldData) throws ExecutionException, InterruptedException { + MinecraftServer console = ((CraftServer) Bukkit.getServer()).getServer(); + + ChunkPos spawnChunk = new ChunkPos( + freshWorld.getChunkSource().randomState().sampler().findSpawnPosition()); + + setRandomSpawnSelection(spawnChunk); + + CompletableFuture initFuture = new CompletableFuture<>(); + + org.bukkit.Location spawnLocation = new org.bukkit.Location( + freshWorld.getWorld(), + spawnChunk.x << 4, + 64, + spawnChunk.z << 4); + FoliaLibHolder.getScheduler().runAtLocation(spawnLocation, task -> { + try { + console.initWorld(freshWorld, worldData, worldData, worldData.worldGenOptions()); + initFuture.complete(true); + } catch (Exception e) { + initFuture.completeExceptionally(e); + } + }); + + return initFuture.get(); + } + + private void setRandomSpawnSelection(ChunkPos spawnChunk) { + try { + Field randomSpawnField = ServerLevel.class.getDeclaredField("randomSpawnSelection"); + randomSpawnField.setAccessible(true); + randomSpawnField.set(freshWorld, spawnChunk); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to set randomSpawnSelection for Folia world initialization", e); + } + } + @Override protected void cleanup() { try { @@ -216,7 +255,7 @@ protected void cleanup() { } catch (Exception ignored) { } - //shutdown chunk provider + // shutdown chunk provider try { Fawe.instance().getQueueHandler().sync(() -> { try { @@ -229,29 +268,35 @@ protected void cleanup() { } catch (Exception ignored) { } - //remove world from server + // remove world from server try { Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap); } catch (Exception ignored) { } - //delete directory + // delete directory try { SafeFiles.tryHardToDeleteDir(tempDir); } catch (Exception ignored) { } } + @Override + protected World getFreshWorld() { + return freshWorld != null ? freshWorld.getWorld() : null; + } + @Override protected IChunkCache initSourceQueueCache() { return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } - //util + // util @SuppressWarnings("unchecked") private void removeWorldFromWorldsMap() { try { - Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + Map map = (Map) serverWorldsField + .get(Bukkit.getServer()); map.remove("faweregentempworld"); } catch (IllegalAccessException e) { throw new RuntimeException(e); @@ -278,8 +323,7 @@ public void updateSpawnPos(@Nonnull ChunkPos spawnPos) { @Override public void onStatusChange( final @Nonnull ChunkPos pos, - @org.jetbrains.annotations.Nullable final net.minecraft.world.level.chunk.status.ChunkStatus status - ) { + @org.jetbrains.annotations.Nullable final net.minecraft.world.level.chunk.status.ChunkStatus status) { } diff --git a/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightFaweAdapter.java index d69c0899fa..aa5bafffd7 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightFaweAdapter.java @@ -10,6 +10,7 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; @@ -21,6 +22,7 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_5.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_5.regen.PaperweightRegen; @@ -129,6 +131,7 @@ import java.util.Objects; import java.util.OptionalInt; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -141,12 +144,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter COMPONENTS_CODEC = DataComponentPatch.CODEC.optionalFieldOf( - "components", DataComponentPatch.EMPTY - ).codec(); + "components", DataComponentPatch.EMPTY).codec(); static { try { - CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave"); + CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class + .getDeclaredMethod("wasAccessibleSinceLastSave"); } catch (NoSuchMethodException ignored) { // may not be present in newer paper versions } } @@ -159,29 +162,31 @@ public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodExcepti public Function blockEntityToCompoundTag() { return blockEntity -> FaweCompoundTag.of( - () -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId(DedicatedServer.getServer().registryAccess())) - ); + () -> (LinCompoundTag) toNativeLin( + blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()))); } public static Property adaptProperty(net.minecraft.world.level.block.state.properties.Property property) { return switch (property) { case net.minecraft.world.level.block.state.properties.BooleanProperty booleanProperty -> - new BooleanProperty(booleanProperty.getName(), ImmutableList.copyOf(booleanProperty.getPossibleValues())); + new BooleanProperty(booleanProperty.getName(), + ImmutableList.copyOf(booleanProperty.getPossibleValues())); case net.minecraft.world.level.block.state.properties.IntegerProperty integerProperty -> - new IntegerProperty(integerProperty.getName(), ImmutableList.copyOf(integerProperty.getPossibleValues())); + new IntegerProperty(integerProperty.getName(), + ImmutableList.copyOf(integerProperty.getPossibleValues())); case net.minecraft.world.level.block.state.properties.EnumProperty enumProperty -> { if (enumProperty.getValueClass() == net.minecraft.core.Direction.class) { yield new DirectionalProperty(enumProperty.getName(), enumProperty.getPossibleValues().stream() .map(StringRepresentable::getSerializedName) .map(s -> s.toUpperCase(Locale.ROOT)) .map(Direction::valueOf) - .toList() - ); + .toList()); } yield new EnumProperty(enumProperty.getName(), enumProperty.getPossibleValues().stream() .map(StringRepresentable::getSerializedName).collect(Collectors.toCollection(ArrayList::new))); } - default -> throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + property.getClass().getSimpleName()); + default -> throw new IllegalArgumentException( + "FastAsyncWorldEdit needs an update to support " + property.getClass().getSimpleName()); }; } @@ -262,7 +267,8 @@ public BlockMaterial getMaterial(BlockType blockType) { @Override public synchronized BlockMaterial getMaterial(BlockState state) { - net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState(); + net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit + .createBlockData(state.getAsString())).getState(); return new PaperweightBlockMaterial(blockState.getBlock(), blockState); } @@ -313,7 +319,8 @@ public BaseBlock getFullBlock(final Location location) { // Read the NBT data BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK); if (blockEntity != null) { - net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()); + net.minecraft.nbt.CompoundTag tag = blockEntity + .saveWithId(DedicatedServer.getServer().registryAccess()); return state.toBaseBlock((LinCompoundTag) toNativeLin(tag)); } } @@ -325,8 +332,7 @@ public BaseBlock getFullBlock(final Location location) { SideEffect.HISTORY, SideEffect.HEIGHTMAPS, SideEffect.LIGHTING, - SideEffect.NEIGHBORS - ); + SideEffect.NEIGHBORS); @Override public Set getSupportedSideEffects() { @@ -352,7 +358,7 @@ public BaseEntity getEntity(org.bukkit.entity.Entity entity) { if (!readEntityIntoTag(mcEntity, minecraftTag)) { return null; } - //add Id for AbstractChangeSet to work + // add Id for AbstractChangeSet to work final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag); final Map> tags = NbtUtils.getLinCompoundTagValues(tag); tags.put("Id", LinStringTag.of(id)); @@ -409,8 +415,7 @@ public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockSt return (char) ibdToOrdinal[id]; } catch (ArrayIndexOutOfBoundsException e1) { LOGGER.error("Attempted to convert {} with ID {} to char. ibdToOrdinal length: {}. Defaulting to air!", - blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1 - ); + blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1); return BlockTypesCache.ReservedIDs.AIR; } } @@ -483,18 +488,22 @@ public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockSt @Override public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { ServerLevel nmsWorld = getServerLevel(world); - ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); + ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), + chunkPacket.getChunkZ()); if (map != null && wasAccessibleSinceLastSave(map)) { boolean flag = false; // PlayerChunk.d players = map.players; - Stream stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag) - */ Stream.empty(); + Stream stream = /* + * players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), + * flag) + */ Stream.empty(); ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle(); stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer) .forEach(entityPlayer -> { synchronized (chunkPacket) { - ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket.getNativePacket(); + ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket + .getNativePacket(); if (nmsPacket == null) { nmsPacket = mapUtil.create(this, chunkPacket); chunkPacket.setNativePacket(nmsPacket); @@ -521,8 +530,7 @@ public boolean canPlaceAt(org.bukkit.World world, BlockVector3 blockVector3, Blo net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); return blockState1.hasPostProcess( getServerLevel(world), - new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z()) - ); + new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z())); } @Override @@ -530,10 +538,8 @@ public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) { final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess(); ItemStack stack = new ItemStack( registryAccess.lookupOrThrow(Registries.ITEM).getValueOrThrow(ResourceKey.create( - Registries.ITEM, ResourceLocation.parse(baseItemStack.getType().id()) - )), - baseItemStack.getAmount() - ); + Registries.ITEM, ResourceLocation.parse(baseItemStack.getType().id()))), + baseItemStack.getAmount()); final CompoundTag nbt = (net.minecraft.nbt.CompoundTag) fromNativeLin(baseItemStack.getNbt()); if (nbt != null) { final DataComponentPatch patch = COMPONENTS_CODEC @@ -562,8 +568,21 @@ protected void postCaptureBlockStates(final ServerLevel serverLevel) { serverLevel.capturedBlockStates.clear(); } + private T syncRegion(World world, BlockVector3 pt, java.util.function.Supplier supplier) { + if (FoliaLibHolder.isFolia()) { + Location location = new Location(world, pt.x(), pt.y(), pt.z()); + CompletableFuture future = new CompletableFuture<>(); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> future.complete(supplier.get())); + return future.join(); + } + return TaskManager.taskManager().sync(supplier); + } + @Override - public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, BlockVector3 pt) { + public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, + BlockVector3 pt) { ServerLevel serverLevel = getServerLevel(world); ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); @@ -573,15 +592,14 @@ public boolean generateFeature(ConfiguredFeatureType feature, World world, EditS .getValue(ResourceLocation.tryParse(feature.id())); FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { if (!configuredFeature.place( populator, generator, serverLevel.random, - new BlockPos(pt.x(), pt.y(), pt.z()) - )) { + new BlockPos(pt.x(), pt.y(), pt.z()))) { return null; } List placedBlocks = new ArrayList<>(populator.getSnapshotBlocks()); @@ -608,7 +626,7 @@ public boolean generateStructure(StructureType type, World world, EditSession ed ChunkPos chunkPos = new ChunkPos(new BlockPos(pt.x(), pt.y(), pt.z())); FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { StructureStart structureStart = structure.generate( @@ -623,20 +641,17 @@ public boolean generateStructure(StructureType type, World world, EditSession ed chunkPos, 0, populator, - biome -> true - ); + biome -> true); if (!structureStart.isValid()) { return null; } else { BoundingBox boundingBox = structureStart.getBoundingBox(); ChunkPos min = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.minX()), - SectionPos.blockToSectionCoord(boundingBox.minZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.minZ())); ChunkPos max = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.maxX()), - SectionPos.blockToSectionCoord(boundingBox.maxZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.maxZ())); ChunkPos.rangeClosed(min, max).forEach((chunkPosx) -> structureStart.placeInChunk( populator, serverLevel.structureManager(), @@ -648,10 +663,8 @@ public boolean generateStructure(StructureType type, World world, EditSession ed chunkPosx.getMinBlockZ(), chunkPosx.getMaxBlockX(), serverLevel.getMaxY(), - chunkPosx.getMaxBlockZ() - ), - chunkPosx - )); + chunkPosx.getMaxBlockZ()), + chunkPosx)); List placedBlocks = new ArrayList<>(populator.getSnapshotBlocks()); placedBlocks.addAll(serverLevel.capturedBlockStates.values()); return placedBlocks; @@ -667,8 +680,7 @@ public boolean generateStructure(StructureType type, World world, EditSession ed private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, - final List placed - ) { + final List placed) { if (placed == null || placed.isEmpty()) { return false; } @@ -678,7 +690,8 @@ private boolean placeFeatureIntoSession( continue; } BlockPos pos = craftBlockState.getPosition(); - editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), BukkitAdapter.adapt(craftBlockState.getBlockData())); + editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), + BukkitAdapter.adapt(craftBlockState.getBlockData())); BlockEntity blockEntity = populator.getBlockEntity(pos); if (blockEntity != null) { CompoundTag tag = blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()); @@ -694,7 +707,8 @@ public void setupFeatures() { // All these features should be the "face" selected Set face_features = Arrays - .stream(new Class[]{AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, VegetationFeatures.class}) + .stream(new Class[] { AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, + VegetationFeatures.class }) .flatMap(c -> Arrays.stream(c.getFields())) .filter(f -> { int modifiers = f.getModifiers(); @@ -753,16 +767,15 @@ protected ServerLevel getServerLevel(final World world) { public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess(); final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); - // We should be fine to perform this later as we're using a deep-copied itemstack (above) + // We should be fine to perform this later as we're using a deep-copied + // itemstack (above) final Supplier tag = () -> COMPONENTS_CODEC.encodeStart( registryAccess.createSerializationContext(NbtOps.INSTANCE), - nmsStack.getComponentsPatch() - ).getOrThrow(); + nmsStack.getComponentsPatch()).getOrThrow(); return new BaseItemStack( BukkitAdapter.asItemType(itemStack.getType()), LazyReference.from(() -> (LinCompoundTag) toNativeLin(tag.get())), - itemStack.getAmount() - ); + itemStack.getAmount()); } @Override @@ -776,7 +789,8 @@ public net.minecraft.nbt.Tag fromNative(Tag foreign) { } @Override - public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception { + public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) + throws Exception { return new PaperweightRegen(bukkitWorld, region, target, options).regenerate(); } diff --git a/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightFaweWorldNativeAccess.java index f5a49fbb67..604214ab26 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightFaweWorldNativeAccess.java @@ -22,6 +22,7 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.redstone.ExperimentalRedstoneUtils; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.event.block.BlockPhysicsEvent; @@ -60,7 +61,7 @@ public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAd this.level = level; // Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging. // - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway. - this.lastTick = new AtomicInteger(MinecraftServer.currentTick); + this.lastTick = new AtomicInteger(getCurrentTick()); } private Level getLevel() { @@ -96,7 +97,7 @@ public synchronized net.minecraft.world.level.block.state.BlockState setBlockSta LevelChunk levelChunk, BlockPos blockPos, net.minecraft.world.level.block.state.BlockState blockState ) { - int currentTick = MinecraftServer.currentTick; + int currentTick = getCurrentTick(); if (Fawe.isMainThread()) { return levelChunk.setBlockState(blockPos, blockState, this.sideEffectSet.shouldApply(SideEffect.UPDATE) ? 0 : 512 @@ -291,4 +292,16 @@ private record CachedChange( } + private int getCurrentTick() { + try { + return MinecraftServer.currentTick; + } catch (NoSuchFieldError e) { + try { + return Bukkit.getCurrentTick(); + } catch (Exception ex) { + return 0; + } + } + } + } diff --git a/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightGetBlocks.java index 82df9b11be..01ab2dedf1 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightGetBlocks.java @@ -11,6 +11,7 @@ import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; @@ -58,6 +59,8 @@ import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.lighting.LevelLightEngine; import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.World; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.CraftBlock; @@ -95,7 +98,8 @@ public class PaperweightGetBlocks extends AbstractBukkitGetBlocks posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); + private static final Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), + v.getZ()); public static final Function NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin .getInstance() .getBukkitImplAdapter()).blockEntityToCompoundTag(); @@ -168,8 +172,9 @@ public BiomeType getBiomeType(int x, int y, int z) { @Override public void removeSectionLighting(int layer, boolean sky) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK) + .getDataLayerData( + sectionPos); if (dataLayer != null) { lightUpdate = true; synchronized (dataLayer) { @@ -196,9 +201,8 @@ public void removeSectionLighting(int layer, boolean sky) { @Override 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))); + BlockEntity blockEntity = getChunk() + .getBlockEntity(new BlockPos((x & 15) + (chunkX << 4), y, (z & 15) + (chunkZ << 4))); if (blockEntity == null) { return null; } @@ -221,19 +225,19 @@ public int getSkyLight(int x, int y, int z) { int alayer = layer - getMinSectionPosition(); if (skyLight[alayer] == null) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = - serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getDataLayerData(sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY) + .getDataLayerData(sectionPos); // If the server hasn't generated the section's NibbleArray yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( LightLayer.BLOCK, sectionPos, - dataLayer - ); + dataLayer); } skyLight[alayer] = dataLayer; } @@ -255,12 +259,13 @@ public int getEmittedLight(int x, int y, int z) { // If the server hasn't generated the section's DataLayer yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); - ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, sectionPos, - dataLayer - ); + ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, + sectionPos, + dataLayer); } blockLight[alayer] = dataLayer; } @@ -333,13 +338,12 @@ protected > T internalCall( Runnable finalizer, int copyKey, LevelChunk nmsChunk, - ServerLevel nmsWorld - ) throws Exception { + ServerLevel nmsWorld) throws Exception { PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null; if (createCopy) { if (copies.containsKey(copyKey)) { throw new IllegalStateException("Copy key already used."); - } + } copies.put(copyKey, copy); } // Remove existing tiles. Create a copy so that we can remove blocks @@ -364,7 +368,18 @@ protected > T internalCall( beacons = new ArrayList<>(); } beacons.add(tile); - PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + if (FoliaLibHolder.isFolia()) { + Location location = new Location( + nmsWorld.getWorld(), + tile.getBlockPos().getX(), + tile.getBlockPos().getY(), + tile.getBlockPos().getZ()); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk)); + } else { + PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + } continue; } nmsChunk.removeBlockEntity(tile.getBlockPos()); @@ -401,33 +416,32 @@ protected > T internalCall( } if (existingSection == null) { - PalettedContainer> biomeData = PaperweightPlatformAdapter.getBiomePalettedContainer( - biomes[setSectionIndex], - biomeHolderIdMap - ); + PalettedContainer> biomeData = PaperweightPlatformAdapter + .getBiomePalettedContainer( + biomes[setSectionIndex], + biomeHolderIdMap); LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, new char[4096], adapter, biomeRegistry, - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { - updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex); + getSectionIndex)) { + updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], + getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; if (existingSection == null) { - LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, + chunkZ, + getSectionIndex); continue; } } @@ -435,8 +449,7 @@ protected > T internalCall( PalettedContainer> paletteBiomes = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); if (paletteBiomes != null) { PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes); } @@ -448,13 +461,16 @@ protected > T internalCall( bitMask |= 1 << getSectionIndex; - // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to - // this chunk GET when #updateGet is called. Future dords, please listen this time. + // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in + // order to write changes to + // this chunk GET when #updateGet is called. Future dords, please listen this + // time. char[] tmp = set.load(layerNo); char[] setArr = new char[tmp.length]; System.arraycopy(tmp, 0, setArr, 0, tmp.length); - // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was + // synchronise on internal section to avoid circular locking with a continuing + // edit if the chunk was // submitted to keep loaded internal chunks to queue target size. synchronized (super.sectionLocks[getSectionIndex]) { @@ -479,37 +495,36 @@ protected > T internalCall( PalettedContainer> biomeData = biomes == null ? new PalettedContainer<>( biomeHolderIdMap, biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap); + PalettedContainer.Strategy.SECTION_BIOMES) + : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], + biomeHolderIdMap); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, setArr, adapter, biomeRegistry, - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { + getSectionIndex)) { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; if (existingSection == null) { LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); continue; } } } - //ensure that the server doesn't try to tick the chunksection while we're editing it. (Again) + // ensure that the server doesn't try to tick the chunksection while we're + // editing it. (Again) PaperweightPlatformAdapter.clearCounts(existingSection); DelegateSemaphore lock = PaperweightPlatformAdapter.applyLock(existingSection); @@ -528,11 +543,12 @@ protected > T internalCall( this.reset(); } else if (!Arrays.equals( update(getSectionIndex, new char[4096], true), - load(layerNo) - )) { + load(layerNo))) { this.reset(layerNo); - /*} else if (lock.isModified()) { - this.reset(layerNo);*/ + /* + * } else if (lock.isModified()) { + * this.reset(layerNo); + */ } } finally { sectionLock.writeLock().unlock(); @@ -541,8 +557,7 @@ protected > T internalCall( PalettedContainer> biomeData = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, @@ -550,19 +565,17 @@ protected > T internalCall( setArr, adapter, biomeRegistry, - biomeData != null ? biomeData : (PalettedContainer>) existingSection.getBiomes() - ); + biomeData != null ? biomeData + : (PalettedContainer>) existingSection.getBiomes()); if (!PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, existingSection, newSection, - getSectionIndex - )) { + getSectionIndex)) { LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); } else { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); } @@ -577,13 +590,11 @@ protected > T internalCall( PaperweightGetBlocks.this.setLightingToGet( set.getLight(), set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); + set.getMaxSectionPosition()); PaperweightGetBlocks.this.setSkyLightingToGet( set.getSkyLight(), set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); + set.getMaxSectionPosition()); List syncTasks = new ArrayList<>(); @@ -596,7 +607,8 @@ protected > T internalCall( final List finalBeacons = beacons; syncTasks.add(() -> { for (BlockEntity beacon : finalBeacons) { - BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), SoundEvents.BEACON_DEACTIVATE); + BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), + SoundEvents.BEACON_DEACTIVATE); new BeaconDeactivatedEvent(CraftBlock.at(beacon.getLevel(), beacon.getBlockPos())).callEvent(); } }); @@ -665,17 +677,33 @@ protected > T internalCall( entity.load(tag); entity.absSnapTo(x, y, z, yaw, pitch); entity.setUUID(NbtUtils.uuid(nativeTag)); - if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { - LOGGER.warn( - "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", - id, - nmsWorld.getWorld().getName(), - x, - y, - z - ); - // Unsuccessful create should not be saved to history - iterator.remove(); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } + }); + } else { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } } } } @@ -695,18 +723,39 @@ protected > T internalCall( final int z = blockHash.z() + bz; final BlockPos pos = new BlockPos(x, y, z); - synchronized (nmsWorld) { - BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); - if (tileEntity == null || tileEntity.isRemoved()) { - nmsWorld.removeBlockEntity(pos); - tileEntity = nmsWorld.getBlockEntity(pos); - } - if (tileEntity != null) { - 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)); - tileEntity.loadWithComponents(tag, DedicatedServer.getServer().registryAccess()); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + tileEntity.loadWithComponents(tag, + DedicatedServer.getServer().registryAccess()); + } + } + }); + } else { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + tileEntity.loadWithComponents(tag, DedicatedServer.getServer().registryAccess()); + } } } } @@ -727,7 +776,8 @@ protected > T internalCall( // send to player if (!set .getSideEffectSet() - .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) { + .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING + || finalMask == 0 && biomes != null) { this.send(); } if (finalizer != null) { @@ -744,8 +794,7 @@ private void updateGet( LevelChunkSection[] chunkSections, LevelChunkSection section, char[] arr, - int layer - ) { + int layer) { try { sectionLock.writeLock().lock(); if (this.getChunk() != nmsChunk) { @@ -759,8 +808,9 @@ private void updateGet( System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); } if (this.sections[layer] != section) { - // Not sure why it's funky, but it's what I did in commit fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords - this.sections[layer] = new LevelChunkSection[]{section}.clone()[0]; + // Not sure why it's funky, but it's what I did in commit + // fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords + this.sections[layer] = new LevelChunkSection[] { section }.clone()[0]; } } finally { sectionLock.writeLock().unlock(); @@ -776,14 +826,19 @@ public void send() { } /** - * Update a given (nullable) data array to the current data stored in the server's chunk, associated with this - * {@link PaperweightPlatformAdapter} instance. Not synchronised to the {@link PaperweightPlatformAdapter} instance as synchronisation - * is handled where necessary in the method, and should otherwise be handled correctly by this method's caller. + * Update a given (nullable) data array to the current data stored in the + * server's chunk, associated with this + * {@link PaperweightPlatformAdapter} instance. Not synchronised to the + * {@link PaperweightPlatformAdapter} instance as synchronisation + * is handled where necessary in the method, and should otherwise be handled + * correctly by this method's caller. * - * @param layer layer index (0 may denote a negative layer in the world, e.g. at y=-32) + * @param layer layer index (0 may denote a negative layer in the world, + * e.g. at y=-32) * @param data array to be updated/filled with data or null * @param aggressive if the cached section array should be re-acquired. - * @return the given array to be filled with data, or a new array if null is given. + * @return the given array to be filled with data, or a new array if null is + * given. */ @Override @SuppressWarnings("unchecked") @@ -813,7 +868,8 @@ public char[] update(int layer, char[] data, boolean aggressive) { return data; } - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get(dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get(dataObject); final int bitsPerEntry = bits.getBits(); final long[] blockStates = bits.getRaw(); @@ -893,10 +949,10 @@ public LevelChunk getChunk() { } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e); throw new FaweException( - TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), + TextComponent + .of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), FaweException.Type.OTHER, - false - ); + false); } } } @@ -904,14 +960,16 @@ public LevelChunk getChunk() { return levelChunk; } - private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, int maxSectionPosition) { + private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, + int maxSectionPosition) { for (int Y = 0; Y <= maxSectionPosition - minSectionPosition; Y++) { if (light[Y] == null) { continue; } SectionPos sectionPos = SectionPos.of(levelChunk.getPos(), Y + minSectionPosition); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer) + .getDataLayerData( + sectionPos); if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; Arrays.fill(LAYER_COUNT, lightLayer == LightLayer.SKY ? (byte) 15 : (byte) 0); @@ -919,8 +977,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( lightLayer, sectionPos, - dataLayer - ); + dataLayer); } synchronized (dataLayer) { for (int x = 0; x < 16; x++) { @@ -940,8 +997,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti private PalettedContainer> setBiomesToPalettedContainer( final BiomeType[][] biomes, final int sectionIndex, - final PalettedContainerRO> data - ) { + final PalettedContainerRO> data) { BiomeType[] sectionBiomes; if (biomes == null || (sectionBiomes = biomes[sectionIndex]) == null) { return null; @@ -958,8 +1014,7 @@ private PalettedContainer> setBiomesToPalettedContainer( x, y, z, - biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType)) - ); + biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType))); } } } @@ -1015,8 +1070,9 @@ public boolean trim(boolean aggressive) { final PalettedContainer blocksExisting = existing.getStates(); final Object dataObject = PaperweightPlatformAdapter.fieldData.get(blocksExisting); - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get( - dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get( + dataObject); int paletteSize; if (palette instanceof LinearPalette || palette instanceof HashMapPalette) { @@ -1026,7 +1082,8 @@ public boolean trim(boolean aggressive) { continue; } if (paletteSize == 1) { - //If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks. + // If the cached palette size is 1 then no blocks can have been changed i.e. do + // not need to update these chunks. continue; } super.trim(false, i); diff --git a/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightPlatformAdapter.java index c0df010c53..89cea319a1 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightPlatformAdapter.java @@ -9,6 +9,7 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.bukkit.WorldEditPlugin; @@ -20,6 +21,7 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import io.papermc.lib.PaperLib; +import io.papermc.paper.util.MCUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.IdMap; @@ -123,13 +125,16 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c")); fieldPalette.setAccessible(true); - fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "g")); + fieldTickingFluidCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingFluidCount", "g")); fieldTickingFluidCount.setAccessible(true); - fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); + fieldTickingBlockCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); fieldTickingBlockCount.setAccessible(true); Field tmpFieldBiomes; try { - // Seems it's sometimes biomes and sometimes "i". Idk this is just easier than having to try to deal with it + // Seems it's sometimes biomes and sometimes "i". Idk this is just easier than + // having to try to deal with it tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("biomes"); // apparently unobf } catch (NoSuchFieldException ignored) { tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("i"); // apparently obf @@ -140,18 +145,19 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod( Refraction.pickName( "getVisibleChunkIfPresent", - "b" - ), long.class - ); + "b"), + long.class); getVisibleChunkIfPresent.setAccessible(true); methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent); if (!PaperLib.isPaper()) { - fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f")); + fieldThreadingDetector = PalettedContainer.class + .getDeclaredField(Refraction.pickName("threadingDetector", "f")); fieldThreadingDetector.setAccessible(true); fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); fieldLock.setAccessible(true); - SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "O")); + SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class + .getDeclaredField(Refraction.pickName("entityManager", "O")); SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true); } else { // in paper, the used methods are synchronized properly @@ -162,17 +168,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( Refraction.pickName("removeGameEventListener", "a"), BlockEntity.class, - ServerLevel.class - ); + ServerLevel.class); removeGameEventListener.setAccessible(true); methodRemoveGameEventListener = lookup.unreflect(removeGameEventListener); Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod( Refraction.pickName( "removeBlockEntityTicker", - "k" - ), BlockPos.class - ); + "k"), + BlockPos.class); removeBlockEntityTicker.setAccessible(true); methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker); @@ -181,8 +185,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method palettedContainerGet = PalettedContainer.class.getDeclaredMethod( Refraction.pickName("get", "a"), - int.class - ); + int.class); palettedContainerGet.setAccessible(true); PALETTED_CONTAINER_GET = lookup.unreflect(palettedContainerGet); } catch (RuntimeException | Error e) { @@ -198,14 +201,13 @@ static boolean setSectionAtomic( LevelChunkSection[] sections, LevelChunkSection expected, LevelChunkSection value, - int layer - ) { + int layer) { return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer); } // There is no point in having a functional semaphore for paper servers. - private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = - ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null)); + private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = ThreadLocal + .withInitial(() -> new DelegateSemaphore(1, null)); static DelegateSemaphore applyLock(LevelChunkSection section) { if (PaperLib.isPaper()) { @@ -260,11 +262,11 @@ public static CompletableFuture ensureLoaded(ServerLevel serverLevel "Unexpected error when getting completed future at chunk {},{}. Returning to default.", chunkX, chunkZ, - e - ); + e); } } - return CompletableFuture.supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); + return CompletableFuture + .supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); } private static LevelChunk toLevelChunk(Chunk chunk) { @@ -301,6 +303,14 @@ private static LevelChunk toLevelChunk(Chunk chunk) { } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { + if (FoliaLibHolder.isFolia()) { + try { + serverLevel.getChunkSource().addTicketWithRadius(ChunkHolderManager.UNLOAD_COOLDOWN, + new ChunkPos(chunkX, chunkZ), 0); + } catch (Exception ignored) { + } + return; + } // Ensure chunk is definitely loaded before applying a ticket io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel .getChunkSource() @@ -337,7 +347,7 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int if (lockHolder.chunkLock == null) { return; } - MinecraftServer.getServer().execute(() -> { + if (FoliaLibHolder.isFolia()) { try { ChunkPos pos = levelChunk.getPos(); ClientboundLevelChunkWithLightPacket packet; @@ -355,14 +365,39 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int levelChunk, nmsWorld.getLightEngine(), null, - null - ); + null); } nearbyPlayers(nmsWorld, pos).forEach(p -> p.connection.send(packet)); } finally { NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); } - }); + } else { + MinecraftServer.getServer().execute(() -> { + try { + ChunkPos pos = levelChunk.getPos(); + ClientboundLevelChunkWithLightPacket packet; + if (PaperLib.isPaper()) { + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null, + false // last false is to not bother with x-ray + ); + } else { + // deprecated on paper - deprecation suppressed + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null); + } + nearbyPlayers(nmsWorld, pos).forEach(p -> p.connection.send(packet)); + } finally { + NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); + } + }); + } } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { @@ -370,15 +405,14 @@ private static List nearbyPlayers(ServerLevel serverLevel, ChunkPo } /* - NMS conversion + * NMS conversion */ public static LevelChunkSection newChunkSection( final int layer, final char[] blocks, CachedBukkitAdapter adapter, Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { return newChunkSection(layer, null, blocks, adapter, biomeRegistry, biomes); } @@ -388,8 +422,7 @@ public static LevelChunkSection newChunkSection( char[] set, CachedBukkitAdapter adapter, Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (set == null) { return newChunkSection(biomeRegistry, biomes); } @@ -445,15 +478,15 @@ public static LevelChunkSection newChunkSection( } // Create palette with data - @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot - final PalettedContainer blockStatePalettedContainer = - new PalettedContainer<>( - Block.BLOCK_STATE_REGISTRY, - PalettedContainer.Strategy.SECTION_STATES, - PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, bitsPerEntry), - nmsBits, - palette - ); + @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility + // with spigot + final PalettedContainer blockStatePalettedContainer = new PalettedContainer<>( + Block.BLOCK_STATE_REGISTRY, + PalettedContainer.Strategy.SECTION_STATES, + PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, + bitsPerEntry), + nmsBits, + palette); if (biomes == null) { IdMap> biomeHolderIdMap = biomeRegistry.asHolderIdMap(); biomes = new PalettedContainer<>( @@ -463,8 +496,7 @@ public static LevelChunkSection newChunkSection( .getBukkitImplAdapter() .getInternalBiomeId( BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ); + PalettedContainer.Strategy.SECTION_BIOMES); } return new LevelChunkSection(blockStatePalettedContainer, biomes); @@ -481,16 +513,14 @@ public static LevelChunkSection newChunkSection( @SuppressWarnings("deprecation") // Only deprecated in paper private static LevelChunkSection newChunkSection( Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (biomes == null) { return new LevelChunkSection(biomeRegistry); } PalettedContainer dataPaletteBlocks = new PalettedContainer<>( Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), - PalettedContainer.Strategy.SECTION_STATES - ); + PalettedContainer.Strategy.SECTION_STATES); return new LevelChunkSection(dataPaletteBlocks, biomes); } @@ -503,17 +533,18 @@ public static void setBiomesToChunkSection(LevelChunkSection section, PalettedCo } /** - * Create a new {@link PalettedContainer}. Should only be used if no biome container existed beforehand. + * Create a new {@link PalettedContainer}. Should only be used if no + * biome container existed beforehand. */ public static PalettedContainer> getBiomePalettedContainer( BiomeType[] biomes, - IdMap> biomeRegistry - ) { + IdMap> biomeRegistry) { if (biomes == null) { return null; } BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - // Don't stream this as typically will see 1-4 biomes; stream overhead is large for the small length + // Don't stream this as typically will see 1-4 biomes; stream overhead is large + // for the small length Map> palette = new HashMap<>(); for (BiomeType biomeType : new LinkedList<>(Arrays.asList(biomes))) { Holder biome; @@ -528,16 +559,14 @@ public static PalettedContainer> getBiomePalettedContainer( int bitsPerEntry = MathMan.log2nlz(biomeCount - 1); Object configuration = PalettedContainer.Strategy.SECTION_STATES.getConfiguration( new FakeIdMapBiome(biomeCount), - bitsPerEntry - ); + bitsPerEntry); if (bitsPerEntry > 3) { bitsPerEntry = MathMan.log2nlz(biomeRegistry.size() - 1); } PalettedContainer> biomePalettedContainer = new PalettedContainer<>( biomeRegistry, biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ); + PalettedContainer.Strategy.SECTION_BIOMES); final Palette> biomePalette; if (bitsPerEntry == 0) { @@ -572,12 +601,11 @@ public static PalettedContainer> getBiomePalettedContainer( int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes final int arrayLength = MathMan.longArrayLength(bitsPerEntryNonZero, 64); - - BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) : new SimpleBitStorage( - bitsPerEntry, - 64, - new long[arrayLength] - ); + BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) + : new SimpleBitStorage( + bitsPerEntry, + 64, + new long[arrayLength]); try { Object data = dataConstructor.newInstance(configuration, bitStorage, biomePalette); @@ -643,11 +671,13 @@ static List getEntities(LevelChunk chunk) { if (PaperLib.isPaper()) { return Optional.ofNullable(chunk.level .moonrise$getEntityLookup() - .getChunk(chunk.locX, chunk.locZ)).map(ChunkEntitySlices::getAllEntities).orElse(Collections.emptyList()); + .getChunk(chunk.locX, chunk.locZ)).map(ChunkEntitySlices::getAllEntities) + .orElse(Collections.emptyList()); } try { - //noinspection unchecked - return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos()); + // noinspection unchecked + return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))) + .getEntities(chunk.getPos()); } catch (IllegalAccessException e) { throw new RuntimeException("Failed to lookup entities [PAPER=false]", e); } diff --git a/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/regen/PaperweightRegen.java index 62335772f1..11a50f6bc0 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/regen/PaperweightRegen.java @@ -1,6 +1,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_5.regen; import com.fastasyncworldedit.bukkit.adapter.Regenerator; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; @@ -41,6 +42,8 @@ import java.nio.file.Path; import java.util.Map; import java.util.OptionalLong; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.function.BooleanSupplier; import java.util.function.Supplier; @@ -52,14 +55,13 @@ public class PaperweightRegen extends Regenerator { private static final Field paperConfigField; private static final Field generatorSettingBaseSupplierField; - static { try { serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField.setAccessible(true); Field tmpPaperConfigField; - try { //only present on paper + try { // only present on paper tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField.setAccessible(true); } catch (Exception e) { @@ -75,7 +77,7 @@ public class PaperweightRegen extends Regenerator { } } - //runtime + // runtime private ServerLevel originalServerWorld; private ServerLevel freshWorld; private LevelStorageSource.LevelStorageAccess session; @@ -86,8 +88,7 @@ public PaperweightRegen( World originalBukkitWorld, Region region, Extent target, - RegenOptions options - ) { + RegenOptions options) { super(originalBukkitWorld, region, target, options); } @@ -109,10 +110,10 @@ protected boolean prepare() { @Override protected boolean initNewWorld() throws Exception { - //world folder + // world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); - //prepare for world init (see upstream implementation for reference) + // prepare for world init (see upstream implementation for reference) org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment(); org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator(); LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir); @@ -132,21 +133,19 @@ protected boolean initNewWorld() throws Exception { originalWorldData.settings.difficulty(), originalWorldData.settings.allowCommands(), originalWorldData.settings.gameRules(), - originalWorldData.settings.getDataConfiguration() - ); + originalWorldData.settings.getDataConfiguration()); - PrimaryLevelData.SpecialWorldProperty specialWorldProperty = - originalWorldData.isFlatWorld() - ? PrimaryLevelData.SpecialWorldProperty.FLAT - : originalWorldData.isDebugWorld() - ? PrimaryLevelData.SpecialWorldProperty.DEBUG - : PrimaryLevelData.SpecialWorldProperty.NONE; - PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable()); + PrimaryLevelData.SpecialWorldProperty specialWorldProperty = originalWorldData.isFlatWorld() + ? PrimaryLevelData.SpecialWorldProperty.FLAT + : originalWorldData.isDebugWorld() + ? PrimaryLevelData.SpecialWorldProperty.DEBUG + : PrimaryLevelData.SpecialWorldProperty.NONE; + PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, + Lifecycle.stable()); BiomeProvider biomeProvider = getBiomeProvider(); - - //init world + // init world freshWorld = Fawe.instance().getQueueHandler().sync((Supplier) () -> new ServerLevel( server, server.executor, @@ -155,8 +154,7 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.dimension(), new LevelStem( originalServerWorld.dimensionTypeRegistration(), - originalServerWorld.getChunkSource().getGenerator() - ), + originalServerWorld.getChunkSource().getGenerator()), new RegenNoOpWorldLoadListener(), originalServerWorld.isDebug(), seed, @@ -165,13 +163,14 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.getRandomSequences(), environment, generator, - biomeProvider - ) { + biomeProvider) { - private final Holder singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess() - .lookupOrThrow(BIOME).asHolderIdMap().byIdOrThrow( - WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) - ) : null; + private final Holder singleBiome = options.hasBiomeType() + ? DedicatedServer.getServer().registryAccess() + .lookupOrThrow(BIOME).asHolderIdMap().byIdOrThrow( + WorldEditPlugin.getInstance().getBukkitImplAdapter() + .getInternalBiomeId(options.getBiomeType())) + : null; @Override public @Nonnull Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { @@ -185,8 +184,7 @@ protected boolean initNewWorld() throws Exception { public void save( final ProgressListener progressListener, final boolean flush, - final boolean savingDisabled - ) { + final boolean savingDisabled) { // noop, spigot } @@ -195,20 +193,61 @@ public void save( final ProgressListener progressListener, final boolean flush, final boolean savingDisabled, - final boolean close - ) { + final boolean close) { // noop, paper } }).get(); freshWorld.noSave = true; removeWorldFromWorldsMap(); - newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name + newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); // rename to original world name if (paperConfigField != null) { paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); } + + if (FoliaLibHolder.isFolia()) { + return initWorldForFolia(newWorldData); + } + return true; } + private boolean initWorldForFolia(PrimaryLevelData worldData) throws ExecutionException, InterruptedException { + MinecraftServer console = ((CraftServer) Bukkit.getServer()).getServer(); + + ChunkPos spawnChunk = new ChunkPos( + freshWorld.getChunkSource().randomState().sampler().findSpawnPosition()); + + setRandomSpawnSelection(spawnChunk); + + CompletableFuture initFuture = new CompletableFuture<>(); + + org.bukkit.Location spawnLocation = new org.bukkit.Location( + freshWorld.getWorld(), + spawnChunk.x << 4, + 64, + spawnChunk.z << 4); + FoliaLibHolder.getScheduler().runAtLocation(spawnLocation, task -> { + try { + console.initWorld(freshWorld, worldData, worldData, worldData.worldGenOptions()); + initFuture.complete(true); + } catch (Exception e) { + initFuture.completeExceptionally(e); + } + }); + + return initFuture.get(); + } + + private void setRandomSpawnSelection(ChunkPos spawnChunk) { + try { + Field randomSpawnField = ServerLevel.class.getDeclaredField("randomSpawnSelection"); + randomSpawnField.setAccessible(true); + randomSpawnField.set(freshWorld, spawnChunk); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to set randomSpawnSelection for Folia world initialization", e); + } + } + @Override protected void cleanup() { try { @@ -216,7 +255,7 @@ protected void cleanup() { } catch (Exception ignored) { } - //shutdown chunk provider + // shutdown chunk provider try { Fawe.instance().getQueueHandler().sync(() -> { try { @@ -229,29 +268,35 @@ protected void cleanup() { } catch (Exception ignored) { } - //remove world from server + // remove world from server try { Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap); } catch (Exception ignored) { } - //delete directory + // delete directory try { SafeFiles.tryHardToDeleteDir(tempDir); } catch (Exception ignored) { } } + @Override + protected World getFreshWorld() { + return freshWorld != null ? freshWorld.getWorld() : null; + } + @Override protected IChunkCache initSourceQueueCache() { return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } - //util + // util @SuppressWarnings("unchecked") private void removeWorldFromWorldsMap() { try { - Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + Map map = (Map) serverWorldsField + .get(Bukkit.getServer()); map.remove("faweregentempworld"); } catch (IllegalAccessException e) { throw new RuntimeException(e); @@ -278,8 +323,7 @@ public void updateSpawnPos(@Nonnull ChunkPos spawnPos) { @Override public void onStatusChange( final @Nonnull ChunkPos pos, - @org.jetbrains.annotations.Nullable final net.minecraft.world.level.chunk.status.ChunkStatus status - ) { + @org.jetbrains.annotations.Nullable final net.minecraft.world.level.chunk.status.ChunkStatus status) { } diff --git a/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightFaweAdapter.java index f347d27cb6..3458b08bdf 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightFaweAdapter.java @@ -2,6 +2,7 @@ import com.fastasyncworldedit.bukkit.adapter.FaweAdapter; import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.entity.LazyBaseEntity; import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor; @@ -21,6 +22,7 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_6.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_6.regen.PaperweightRegen; @@ -131,6 +133,7 @@ import java.util.Objects; import java.util.OptionalInt; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -144,12 +147,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter COMPONENTS_CODEC = DataComponentPatch.CODEC.optionalFieldOf( - "components", DataComponentPatch.EMPTY - ).codec(); + "components", DataComponentPatch.EMPTY).codec(); static { try { - CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave"); + CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class + .getDeclaredMethod("wasAccessibleSinceLastSave"); } catch (NoSuchMethodException ignored) { // may not be present in newer paper versions } } @@ -163,34 +166,36 @@ public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodExcepti public Function blockEntityToCompoundTag() { return blockEntity -> FaweCompoundTag.of( () -> { - // TODO (VI/O) it might be possible to have a custom ValueOutput that writes into a LinCompoundTag.Builder - // directly + // TODO (VI/O) it might be possible to have a custom ValueOutput that writes + // into a LinCompoundTag.Builder + // directly TagValueOutput output = createOutput(); blockEntity.saveWithId(output); return (LinCompoundTag) toNativeLin(output.buildResult()); - } - ); + }); } public static Property adaptProperty(net.minecraft.world.level.block.state.properties.Property property) { return switch (property) { case net.minecraft.world.level.block.state.properties.BooleanProperty booleanProperty -> - new BooleanProperty(booleanProperty.getName(), ImmutableList.copyOf(booleanProperty.getPossibleValues())); + new BooleanProperty(booleanProperty.getName(), + ImmutableList.copyOf(booleanProperty.getPossibleValues())); case net.minecraft.world.level.block.state.properties.IntegerProperty integerProperty -> - new IntegerProperty(integerProperty.getName(), ImmutableList.copyOf(integerProperty.getPossibleValues())); + new IntegerProperty(integerProperty.getName(), + ImmutableList.copyOf(integerProperty.getPossibleValues())); case net.minecraft.world.level.block.state.properties.EnumProperty enumProperty -> { if (enumProperty.getValueClass() == net.minecraft.core.Direction.class) { yield new DirectionalProperty(enumProperty.getName(), enumProperty.getPossibleValues().stream() .map(StringRepresentable::getSerializedName) .map(s -> s.toUpperCase(Locale.ROOT)) .map(Direction::valueOf) - .toList() - ); + .toList()); } yield new EnumProperty(enumProperty.getName(), enumProperty.getPossibleValues().stream() .map(StringRepresentable::getSerializedName).collect(Collectors.toCollection(ArrayList::new))); } - default -> throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + property.getClass().getSimpleName()); + default -> throw new IllegalArgumentException( + "FastAsyncWorldEdit needs an update to support " + property.getClass().getSimpleName()); }; } @@ -271,7 +276,8 @@ public BlockMaterial getMaterial(BlockType blockType) { @Override public synchronized BlockMaterial getMaterial(BlockState state) { - net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState(); + net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit + .createBlockData(state.getAsString())).getState(); return new PaperweightBlockMaterial(blockState.getBlock(), blockState); } @@ -336,8 +342,7 @@ public BaseBlock getFullBlock(final Location location) { SideEffect.HISTORY, SideEffect.HEIGHTMAPS, SideEffect.LIGHTING, - SideEffect.NEIGHBORS - ); + SideEffect.NEIGHBORS); @Override public Set getSupportedSideEffects() { @@ -363,7 +368,7 @@ public BaseEntity getEntity(org.bukkit.entity.Entity entity) { if (!readEntityIntoTag(mcEntity, minecraftTag)) { return null; } - //add Id for AbstractChangeSet to work + // add Id for AbstractChangeSet to work final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag); final Map> tags = NbtUtils.getLinCompoundTagValues(tag); tags.put("Id", LinStringTag.of(id)); @@ -420,8 +425,7 @@ public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockSt return (char) ibdToOrdinal[id]; } catch (ArrayIndexOutOfBoundsException e1) { LOGGER.error("Attempted to convert {} with ID {} to char. ibdToOrdinal length: {}. Defaulting to air!", - blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1 - ); + blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1); return BlockTypesCache.ReservedIDs.AIR; } } @@ -494,18 +498,22 @@ public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockSt @Override public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { ServerLevel nmsWorld = getServerLevel(world); - ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); + ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), + chunkPacket.getChunkZ()); if (map != null && wasAccessibleSinceLastSave(map)) { boolean flag = false; // PlayerChunk.d players = map.players; - Stream stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag) - */ Stream.empty(); + Stream stream = /* + * players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), + * flag) + */ Stream.empty(); ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle(); stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer) .forEach(entityPlayer -> { synchronized (chunkPacket) { - ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket.getNativePacket(); + ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket + .getNativePacket(); if (nmsPacket == null) { nmsPacket = mapUtil.create(this, chunkPacket); chunkPacket.setNativePacket(nmsPacket); @@ -532,8 +540,7 @@ public boolean canPlaceAt(org.bukkit.World world, BlockVector3 blockVector3, Blo net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); return blockState1.hasPostProcess( getServerLevel(world), - new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z()) - ); + new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z())); } @Override @@ -541,10 +548,8 @@ public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) { final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess(); ItemStack stack = new ItemStack( registryAccess.lookupOrThrow(Registries.ITEM).getValueOrThrow(ResourceKey.create( - Registries.ITEM, ResourceLocation.parse(baseItemStack.getType().id()) - )), - baseItemStack.getAmount() - ); + Registries.ITEM, ResourceLocation.parse(baseItemStack.getType().id()))), + baseItemStack.getAmount()); final CompoundTag nbt = (net.minecraft.nbt.CompoundTag) fromNativeLin(baseItemStack.getNbt()); if (nbt != null) { final DataComponentPatch patch = COMPONENTS_CODEC @@ -573,8 +578,21 @@ protected void postCaptureBlockStates(final ServerLevel serverLevel) { serverLevel.capturedBlockStates.clear(); } + private T syncRegion(World world, BlockVector3 pt, java.util.function.Supplier supplier) { + if (FoliaLibHolder.isFolia()) { + Location location = new Location(world, pt.x(), pt.y(), pt.z()); + CompletableFuture future = new CompletableFuture<>(); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> future.complete(supplier.get())); + return future.join(); + } + return TaskManager.taskManager().sync(supplier); + } + @Override - public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, BlockVector3 pt) { + public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, + BlockVector3 pt) { ServerLevel serverLevel = getServerLevel(world); ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); @@ -584,15 +602,14 @@ public boolean generateFeature(ConfiguredFeatureType feature, World world, EditS .getValue(ResourceLocation.tryParse(feature.id())); FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { if (!configuredFeature.place( populator, generator, serverLevel.random, - new BlockPos(pt.x(), pt.y(), pt.z()) - )) { + new BlockPos(pt.x(), pt.y(), pt.z()))) { return null; } List placedBlocks = new ArrayList<>(populator.getSnapshotBlocks()); @@ -626,7 +643,7 @@ public boolean canTransformBlocks() { }; FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); access.setDelegate(populator); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { StructureStart structureStart = structure.generate( @@ -641,20 +658,17 @@ public boolean canTransformBlocks() { chunkPos, 0, populator, - biome -> true - ); + biome -> true); if (!structureStart.isValid()) { return null; } else { BoundingBox boundingBox = structureStart.getBoundingBox(); ChunkPos min = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.minX()), - SectionPos.blockToSectionCoord(boundingBox.minZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.minZ())); ChunkPos max = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.maxX()), - SectionPos.blockToSectionCoord(boundingBox.maxZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.maxZ())); ChunkPos.rangeClosed(min, max).forEach((chunkPosx) -> structureStart.placeInChunk( access, serverLevel.structureManager(), @@ -665,9 +679,8 @@ public boolean canTransformBlocks() { serverLevel.getMinY(), chunkPosx.getMinBlockZ(), chunkPosx.getMaxBlockX(), - serverLevel.getMaxY(), chunkPosx.getMaxBlockZ() - ), chunkPosx - )); + serverLevel.getMaxY(), chunkPosx.getMaxBlockZ()), + chunkPosx)); List placedBlocks = new ArrayList<>(populator.getSnapshotBlocks()); placedBlocks.addAll(serverLevel.capturedBlockStates.values()); return placedBlocks; @@ -683,8 +696,7 @@ public boolean canTransformBlocks() { private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, - final List placed - ) { + final List placed) { if (placed == null || placed.isEmpty()) { return false; } @@ -694,13 +706,15 @@ private boolean placeFeatureIntoSession( continue; } BlockPos pos = craftBlockState.getPosition(); - editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), BukkitAdapter.adapt(craftBlockState.getBlockData())); + editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), + BukkitAdapter.adapt(craftBlockState.getBlockData())); BlockEntity blockEntity = populator.getBlockEntity(pos); if (blockEntity != null) { // TODO (VI/O) TagValueOutput output = createOutput(); blockEntity.saveWithId(output); - editSession.setTile(pos.getX(), pos.getY(), pos.getZ(), (com.sk89q.jnbt.CompoundTag) toNative(output.buildResult())); + editSession.setTile(pos.getX(), pos.getY(), pos.getZ(), + (com.sk89q.jnbt.CompoundTag) toNative(output.buildResult())); } } return true; @@ -712,7 +726,8 @@ public void setupFeatures() { // All these features should be the "face" selected Set face_features = Arrays - .stream(new Class[]{AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, VegetationFeatures.class}) + .stream(new Class[] { AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, + VegetationFeatures.class }) .flatMap(c -> Arrays.stream(c.getFields())) .filter(f -> { int modifiers = f.getModifiers(); @@ -762,7 +777,6 @@ public void setupFeatures() { } } - @Override protected ServerLevel getServerLevel(final World world) { return ((CraftWorld) world).getHandle(); @@ -772,16 +786,15 @@ protected ServerLevel getServerLevel(final World world) { public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess(); final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); - // We should be fine to perform this later as we're using a deep-copied itemstack (above) + // We should be fine to perform this later as we're using a deep-copied + // itemstack (above) final Supplier tag = () -> COMPONENTS_CODEC.encodeStart( registryAccess.createSerializationContext(NbtOps.INSTANCE), - nmsStack.getComponentsPatch() - ).getOrThrow(); + nmsStack.getComponentsPatch()).getOrThrow(); return new BaseItemStack( BukkitAdapter.asItemType(itemStack.getType()), LazyReference.from(() -> (LinCompoundTag) toNativeLin(tag.get())), - itemStack.getAmount() - ); + itemStack.getAmount()); } @Override @@ -795,7 +808,8 @@ public net.minecraft.nbt.Tag fromNative(Tag foreign) { } @Override - public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception { + public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) + throws Exception { return new PaperweightRegen(bukkitWorld, region, target, options).regenerate(); } diff --git a/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightFaweWorldNativeAccess.java index 36370d286f..eebf3b36fc 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightFaweWorldNativeAccess.java @@ -22,6 +22,7 @@ import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.redstone.ExperimentalRedstoneUtils; import net.minecraft.world.level.storage.ValueInput; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.event.block.BlockPhysicsEvent; @@ -62,7 +63,7 @@ public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAd this.level = level; // Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging. // - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway. - this.lastTick = new AtomicInteger(MinecraftServer.currentTick); + this.lastTick = new AtomicInteger(getCurrentTick()); } private Level getLevel() { @@ -98,7 +99,7 @@ public synchronized net.minecraft.world.level.block.state.BlockState setBlockSta LevelChunk levelChunk, BlockPos blockPos, net.minecraft.world.level.block.state.BlockState blockState ) { - int currentTick = MinecraftServer.currentTick; + int currentTick = getCurrentTick(); if (Fawe.isMainThread()) { return levelChunk.setBlockState( blockPos, blockState, @@ -309,4 +310,16 @@ private record CachedChange( } + private int getCurrentTick() { + try { + return MinecraftServer.currentTick; + } catch (NoSuchFieldError e) { + try { + return Bukkit.getCurrentTick(); + } catch (Exception ex) { + return 0; + } + } + } + } diff --git a/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightGetBlocks.java index 8146620d38..c725d96d31 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightGetBlocks.java @@ -11,6 +11,7 @@ import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; @@ -59,6 +60,8 @@ import net.minecraft.world.level.storage.TagValueOutput; import net.minecraft.world.level.storage.ValueInput; import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.World; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.CraftBlock; @@ -98,7 +101,8 @@ public class PaperweightGetBlocks extends AbstractBukkitGetBlocks posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); + private static final Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), + v.getZ()); public static final Function NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin .getInstance() .getBukkitImplAdapter()).blockEntityToCompoundTag(); @@ -171,8 +175,9 @@ public BiomeType getBiomeType(int x, int y, int z) { @Override public void removeSectionLighting(int layer, boolean sky) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK) + .getDataLayerData( + sectionPos); if (dataLayer != null) { lightUpdate = true; synchronized (dataLayer) { @@ -199,9 +204,8 @@ public void removeSectionLighting(int layer, boolean sky) { @Override 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))); + BlockEntity blockEntity = getChunk() + .getBlockEntity(new BlockPos((x & 15) + (chunkX << 4), y, (z & 15) + (chunkZ << 4))); if (blockEntity == null) { return null; } @@ -224,19 +228,19 @@ public int getSkyLight(int x, int y, int z) { int alayer = layer - getMinSectionPosition(); if (skyLight[alayer] == null) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = - serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getDataLayerData(sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY) + .getDataLayerData(sectionPos); // If the server hasn't generated the section's NibbleArray yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( LightLayer.BLOCK, sectionPos, - dataLayer - ); + dataLayer); } skyLight[alayer] = dataLayer; } @@ -258,12 +262,13 @@ public int getEmittedLight(int x, int y, int z) { // If the server hasn't generated the section's DataLayer yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); - ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, sectionPos, - dataLayer - ); + ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, + sectionPos, + dataLayer); } blockLight[alayer] = dataLayer; } @@ -337,13 +342,12 @@ protected > T internalCall( Runnable finalizer, int copyKey, LevelChunk nmsChunk, - ServerLevel nmsWorld - ) throws Exception { + ServerLevel nmsWorld) throws Exception { PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null; if (createCopy) { if (copies.containsKey(copyKey)) { throw new IllegalStateException("Copy key already used."); - } + } copies.put(copyKey, copy); } // Remove existing tiles. Create a copy so that we can remove blocks @@ -368,7 +372,18 @@ protected > T internalCall( beacons = new ArrayList<>(); } beacons.add(tile); - PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + if (FoliaLibHolder.isFolia()) { + Location location = new Location( + nmsWorld.getWorld(), + tile.getBlockPos().getX(), + tile.getBlockPos().getY(), + tile.getBlockPos().getZ()); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk)); + } else { + PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + } continue; } nmsChunk.removeBlockEntity(tile.getBlockPos()); @@ -405,33 +420,32 @@ protected > T internalCall( } if (existingSection == null) { - PalettedContainer> biomeData = PaperweightPlatformAdapter.getBiomePalettedContainer( - biomes[setSectionIndex], - biomeHolderIdMap - ); + PalettedContainer> biomeData = PaperweightPlatformAdapter + .getBiomePalettedContainer( + biomes[setSectionIndex], + biomeHolderIdMap); LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, new char[4096], adapter, biomeRegistry, - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { - updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex); + getSectionIndex)) { + updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], + getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; if (existingSection == null) { - LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, + chunkZ, + getSectionIndex); continue; } } @@ -439,8 +453,7 @@ protected > T internalCall( PalettedContainer> paletteBiomes = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); if (paletteBiomes != null) { PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes); } @@ -452,13 +465,16 @@ protected > T internalCall( bitMask |= 1 << getSectionIndex; - // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to - // this chunk GET when #updateGet is called. Future dords, please listen this time. + // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in + // order to write changes to + // this chunk GET when #updateGet is called. Future dords, please listen this + // time. char[] tmp = set.load(layerNo); char[] setArr = new char[tmp.length]; System.arraycopy(tmp, 0, setArr, 0, tmp.length); - // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was + // synchronise on internal section to avoid circular locking with a continuing + // edit if the chunk was // submitted to keep loaded internal chunks to queue target size. synchronized (super.sectionLocks[getSectionIndex]) { @@ -483,37 +499,36 @@ protected > T internalCall( PalettedContainer> biomeData = biomes == null ? new PalettedContainer<>( biomeHolderIdMap, biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap); + PalettedContainer.Strategy.SECTION_BIOMES) + : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], + biomeHolderIdMap); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, setArr, adapter, biomeRegistry, - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { + getSectionIndex)) { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; if (existingSection == null) { LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); continue; } } } - //ensure that the server doesn't try to tick the chunksection while we're editing it. (Again) + // ensure that the server doesn't try to tick the chunksection while we're + // editing it. (Again) PaperweightPlatformAdapter.clearCounts(existingSection); DelegateSemaphore lock = PaperweightPlatformAdapter.applyLock(existingSection); @@ -532,11 +547,12 @@ protected > T internalCall( this.reset(); } else if (!Arrays.equals( update(getSectionIndex, new char[4096], true), - load(layerNo) - )) { + load(layerNo))) { this.reset(layerNo); - /*} else if (lock.isModified()) { - this.reset(layerNo);*/ + /* + * } else if (lock.isModified()) { + * this.reset(layerNo); + */ } } finally { sectionLock.writeLock().unlock(); @@ -545,8 +561,7 @@ protected > T internalCall( PalettedContainer> biomeData = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, @@ -554,19 +569,17 @@ protected > T internalCall( setArr, adapter, biomeRegistry, - biomeData != null ? biomeData : (PalettedContainer>) existingSection.getBiomes() - ); + biomeData != null ? biomeData + : (PalettedContainer>) existingSection.getBiomes()); if (!PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, existingSection, newSection, - getSectionIndex - )) { + getSectionIndex)) { LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); } else { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); } @@ -581,13 +594,11 @@ protected > T internalCall( PaperweightGetBlocks.this.setLightingToGet( set.getLight(), set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); + set.getMaxSectionPosition()); PaperweightGetBlocks.this.setSkyLightingToGet( set.getSkyLight(), set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); + set.getMaxSectionPosition()); List syncTasks = new ArrayList<>(); @@ -600,7 +611,8 @@ protected > T internalCall( final List finalBeacons = beacons; syncTasks.add(() -> { for (BlockEntity beacon : finalBeacons) { - BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), SoundEvents.BEACON_DEACTIVATE); + BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), + SoundEvents.BEACON_DEACTIVATE); new BeaconDeactivatedEvent(CraftBlock.at(beacon.getLevel(), beacon.getBlockPos())).callEvent(); } }); @@ -671,17 +683,33 @@ protected > T internalCall( entity.load(input); entity.absSnapTo(x, y, z, yaw, pitch); entity.setUUID(NbtUtils.uuid(nativeTag)); - if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { - LOGGER.warn( - "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", - id, - nmsWorld.getWorld().getName(), - x, - y, - z - ); - // Unsuccessful create should not be saved to history - iterator.remove(); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } + }); + } else { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } } } } @@ -701,20 +729,42 @@ protected > T internalCall( final int z = blockHash.z() + bz; final BlockPos pos = new BlockPos(x, y, z); - synchronized (nmsWorld) { - BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); - if (tileEntity == null || tileEntity.isRemoved()) { - nmsWorld.removeBlockEntity(pos); - tileEntity = nmsWorld.getBlockEntity(pos); - } - if (tileEntity != null) { - 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)); - // TODO (VI/O) - ValueInput input = createInput(tag); - tileEntity.loadWithComponents(input); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + // TODO (VI/O) + ValueInput input = createInput(tag); + tileEntity.loadWithComponents(input); + } + } + }); + } else { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + // TODO (VI/O) + ValueInput input = createInput(tag); + tileEntity.loadWithComponents(input); + } } } } @@ -735,7 +785,8 @@ protected > T internalCall( // send to player if (!set .getSideEffectSet() - .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) { + .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING + || finalMask == 0 && biomes != null) { this.send(); } if (finalizer != null) { @@ -752,8 +803,7 @@ private void updateGet( LevelChunkSection[] chunkSections, LevelChunkSection section, char[] arr, - int layer - ) { + int layer) { try { sectionLock.writeLock().lock(); if (this.getChunk() != nmsChunk) { @@ -767,8 +817,9 @@ private void updateGet( System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); } if (this.sections[layer] != section) { - // Not sure why it's funky, but it's what I did in commit fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords - this.sections[layer] = new LevelChunkSection[]{section}.clone()[0]; + // Not sure why it's funky, but it's what I did in commit + // fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords + this.sections[layer] = new LevelChunkSection[] { section }.clone()[0]; } } finally { sectionLock.writeLock().unlock(); @@ -784,14 +835,19 @@ public void send() { } /** - * Update a given (nullable) data array to the current data stored in the server's chunk, associated with this - * {@link PaperweightPlatformAdapter} instance. Not synchronised to the {@link PaperweightPlatformAdapter} instance as synchronisation - * is handled where necessary in the method, and should otherwise be handled correctly by this method's caller. + * Update a given (nullable) data array to the current data stored in the + * server's chunk, associated with this + * {@link PaperweightPlatformAdapter} instance. Not synchronised to the + * {@link PaperweightPlatformAdapter} instance as synchronisation + * is handled where necessary in the method, and should otherwise be handled + * correctly by this method's caller. * - * @param layer layer index (0 may denote a negative layer in the world, e.g. at y=-32) + * @param layer layer index (0 may denote a negative layer in the world, + * e.g. at y=-32) * @param data array to be updated/filled with data or null * @param aggressive if the cached section array should be re-acquired. - * @return the given array to be filled with data, or a new array if null is given. + * @return the given array to be filled with data, or a new array if null is + * given. */ @Override @SuppressWarnings("unchecked") @@ -821,7 +877,8 @@ public char[] update(int layer, char[] data, boolean aggressive) { return data; } - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get(dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get(dataObject); final int bitsPerEntry = bits.getBits(); final long[] blockStates = bits.getRaw(); @@ -901,10 +958,10 @@ public LevelChunk getChunk() { } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e); throw new FaweException( - TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), + TextComponent + .of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), FaweException.Type.OTHER, - false - ); + false); } } } @@ -912,14 +969,16 @@ public LevelChunk getChunk() { return levelChunk; } - private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, int maxSectionPosition) { + private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, + int maxSectionPosition) { for (int Y = 0; Y <= maxSectionPosition - minSectionPosition; Y++) { if (light[Y] == null) { continue; } SectionPos sectionPos = SectionPos.of(levelChunk.getPos(), Y + minSectionPosition); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer) + .getDataLayerData( + sectionPos); if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; Arrays.fill(LAYER_COUNT, lightLayer == LightLayer.SKY ? (byte) 15 : (byte) 0); @@ -927,8 +986,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( lightLayer, sectionPos, - dataLayer - ); + dataLayer); } synchronized (dataLayer) { for (int x = 0; x < 16; x++) { @@ -948,8 +1006,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti private PalettedContainer> setBiomesToPalettedContainer( final BiomeType[][] biomes, final int sectionIndex, - final PalettedContainerRO> data - ) { + final PalettedContainerRO> data) { BiomeType[] sectionBiomes; if (biomes == null || (sectionBiomes = biomes[sectionIndex]) == null) { return null; @@ -966,8 +1023,7 @@ private PalettedContainer> setBiomesToPalettedContainer( x, y, z, - biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType)) - ); + biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType))); } } } @@ -1023,8 +1079,9 @@ public boolean trim(boolean aggressive) { final PalettedContainer blocksExisting = existing.getStates(); final Object dataObject = PaperweightPlatformAdapter.fieldData.get(blocksExisting); - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get( - dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get( + dataObject); int paletteSize; if (palette instanceof LinearPalette || palette instanceof HashMapPalette) { @@ -1034,7 +1091,8 @@ public boolean trim(boolean aggressive) { continue; } if (paletteSize == 1) { - //If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks. + // If the cached palette size is 1 then no blocks can have been changed i.e. do + // not need to update these chunks. continue; } super.trim(false, i); diff --git a/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightPlatformAdapter.java index 99864d45f7..604fec5b8c 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightPlatformAdapter.java @@ -9,6 +9,7 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.bukkit.WorldEditPlugin; @@ -20,6 +21,7 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import io.papermc.lib.PaperLib; +import io.papermc.paper.util.MCUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.IdMap; @@ -129,13 +131,16 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c")); fieldPalette.setAccessible(true); - fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "g")); + fieldTickingFluidCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingFluidCount", "g")); fieldTickingFluidCount.setAccessible(true); - fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); + fieldTickingBlockCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); fieldTickingBlockCount.setAccessible(true); Field tmpFieldBiomes; try { - // Seems it's sometimes biomes and sometimes "i". Idk this is just easier than having to try to deal with it + // Seems it's sometimes biomes and sometimes "i". Idk this is just easier than + // having to try to deal with it tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("biomes"); // apparently unobf } catch (NoSuchFieldException ignored) { tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("i"); // apparently obf @@ -146,18 +151,19 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod( Refraction.pickName( "getVisibleChunkIfPresent", - "b" - ), long.class - ); + "b"), + long.class); getVisibleChunkIfPresent.setAccessible(true); methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent); if (!PaperLib.isPaper()) { - fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f")); + fieldThreadingDetector = PalettedContainer.class + .getDeclaredField(Refraction.pickName("threadingDetector", "f")); fieldThreadingDetector.setAccessible(true); fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); fieldLock.setAccessible(true); - SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "P")); + SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class + .getDeclaredField(Refraction.pickName("entityManager", "P")); SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true); } else { // in paper, the used methods are synchronized properly @@ -168,17 +174,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( Refraction.pickName("removeGameEventListener", "a"), BlockEntity.class, - ServerLevel.class - ); + ServerLevel.class); removeGameEventListener.setAccessible(true); methodRemoveGameEventListener = lookup.unreflect(removeGameEventListener); Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod( Refraction.pickName( "removeBlockEntityTicker", - "k" - ), BlockPos.class - ); + "k"), + BlockPos.class); removeBlockEntityTicker.setAccessible(true); methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker); @@ -187,8 +191,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method palettedContainerGet = PalettedContainer.class.getDeclaredMethod( Refraction.pickName("get", "a"), - int.class - ); + int.class); palettedContainerGet.setAccessible(true); PALETTED_CONTAINER_GET = lookup.unreflect(palettedContainerGet); } catch (RuntimeException | Error e) { @@ -201,24 +204,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { public static TagValueOutput createOutput() { return TagValueOutput.createWithContext( ProblemReporter.DISCARDING, - DedicatedServer.getServer().registryAccess() - ); + DedicatedServer.getServer().registryAccess()); } public static TagValueOutput createOutput(CompoundTag compoundTag) { return TagValueOutput.createWrappingWithContext( ProblemReporter.DISCARDING, DedicatedServer.getServer().registryAccess(), - compoundTag - ); + compoundTag); } public static ValueInput createInput(CompoundTag nativeTag) { return TagValueInput.create( ProblemReporter.DISCARDING, DedicatedServer.getServer().registryAccess(), - nativeTag - ); + nativeTag); } static boolean setSectionAtomic( @@ -227,14 +227,13 @@ static boolean setSectionAtomic( LevelChunkSection[] sections, LevelChunkSection expected, LevelChunkSection value, - int layer - ) { + int layer) { return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer); } // There is no point in having a functional semaphore for paper servers. - private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = - ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null)); + private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = ThreadLocal + .withInitial(() -> new DelegateSemaphore(1, null)); static DelegateSemaphore applyLock(LevelChunkSection section) { if (PaperLib.isPaper()) { @@ -289,11 +288,11 @@ public static CompletableFuture ensureLoaded(ServerLevel serverLevel "Unexpected error when getting completed future at chunk {},{}. Returning to default.", chunkX, chunkZ, - e - ); + e); } } - return CompletableFuture.supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); + return CompletableFuture + .supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); } private static LevelChunk toLevelChunk(Chunk chunk) { @@ -330,6 +329,14 @@ private static LevelChunk toLevelChunk(Chunk chunk) { } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { + if (FoliaLibHolder.isFolia()) { + try { + serverLevel.getChunkSource().addTicketWithRadius(ChunkHolderManager.UNLOAD_COOLDOWN, + new ChunkPos(chunkX, chunkZ), 0); + } catch (Exception ignored) { + } + return; + } // Ensure chunk is definitely loaded before applying a ticket io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel .getChunkSource() @@ -366,7 +373,7 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int if (lockHolder.chunkLock == null) { return; } - MinecraftServer.getServer().execute(() -> { + if (FoliaLibHolder.isFolia()) { try { ChunkPos pos = levelChunk.getPos(); ClientboundLevelChunkWithLightPacket packet; @@ -384,14 +391,39 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int levelChunk, nmsWorld.getLightEngine(), null, - null - ); + null); } nearbyPlayers(nmsWorld, pos).forEach(p -> p.connection.send(packet)); } finally { NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); } - }); + } else { + MinecraftServer.getServer().execute(() -> { + try { + ChunkPos pos = levelChunk.getPos(); + ClientboundLevelChunkWithLightPacket packet; + if (PaperLib.isPaper()) { + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null, + false // last false is to not bother with x-ray + ); + } else { + // deprecated on paper - deprecation suppressed + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null); + } + nearbyPlayers(nmsWorld, pos).forEach(p -> p.connection.send(packet)); + } finally { + NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); + } + }); + } } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { @@ -399,15 +431,14 @@ private static List nearbyPlayers(ServerLevel serverLevel, ChunkPo } /* - NMS conversion + * NMS conversion */ public static LevelChunkSection newChunkSection( final int layer, final char[] blocks, CachedBukkitAdapter adapter, Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { return newChunkSection(layer, null, blocks, adapter, biomeRegistry, biomes); } @@ -417,8 +448,7 @@ public static LevelChunkSection newChunkSection( char[] set, CachedBukkitAdapter adapter, Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (set == null) { return newChunkSection(biomeRegistry, biomes); } @@ -474,15 +504,15 @@ public static LevelChunkSection newChunkSection( } // Create palette with data - @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot - final PalettedContainer blockStatePalettedContainer = - new PalettedContainer<>( - Block.BLOCK_STATE_REGISTRY, - PalettedContainer.Strategy.SECTION_STATES, - PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, bitsPerEntry), - nmsBits, - palette - ); + @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility + // with spigot + final PalettedContainer blockStatePalettedContainer = new PalettedContainer<>( + Block.BLOCK_STATE_REGISTRY, + PalettedContainer.Strategy.SECTION_STATES, + PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, + bitsPerEntry), + nmsBits, + palette); if (biomes == null) { IdMap> biomeHolderIdMap = biomeRegistry.asHolderIdMap(); biomes = new PalettedContainer<>( @@ -492,8 +522,7 @@ public static LevelChunkSection newChunkSection( .getBukkitImplAdapter() .getInternalBiomeId( BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ); + PalettedContainer.Strategy.SECTION_BIOMES); } return new LevelChunkSection(blockStatePalettedContainer, biomes); @@ -510,16 +539,14 @@ public static LevelChunkSection newChunkSection( @SuppressWarnings("deprecation") // Only deprecated in paper private static LevelChunkSection newChunkSection( Registry biomeRegistry, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (biomes == null) { return new LevelChunkSection(biomeRegistry); } PalettedContainer dataPaletteBlocks = new PalettedContainer<>( Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), - PalettedContainer.Strategy.SECTION_STATES - ); + PalettedContainer.Strategy.SECTION_STATES); return new LevelChunkSection(dataPaletteBlocks, biomes); } @@ -532,17 +559,18 @@ public static void setBiomesToChunkSection(LevelChunkSection section, PalettedCo } /** - * Create a new {@link PalettedContainer}. Should only be used if no biome container existed beforehand. + * Create a new {@link PalettedContainer}. Should only be used if no + * biome container existed beforehand. */ public static PalettedContainer> getBiomePalettedContainer( BiomeType[] biomes, - IdMap> biomeRegistry - ) { + IdMap> biomeRegistry) { if (biomes == null) { return null; } BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - // Don't stream this as typically will see 1-4 biomes; stream overhead is large for the small length + // Don't stream this as typically will see 1-4 biomes; stream overhead is large + // for the small length Map> palette = new HashMap<>(); for (BiomeType biomeType : new LinkedList<>(Arrays.asList(biomes))) { Holder biome; @@ -557,16 +585,14 @@ public static PalettedContainer> getBiomePalettedContainer( int bitsPerEntry = MathMan.log2nlz(biomeCount - 1); Object configuration = PalettedContainer.Strategy.SECTION_STATES.getConfiguration( new FakeIdMapBiome(biomeCount), - bitsPerEntry - ); + bitsPerEntry); if (bitsPerEntry > 3) { bitsPerEntry = MathMan.log2nlz(biomeRegistry.size() - 1); } PalettedContainer> biomePalettedContainer = new PalettedContainer<>( biomeRegistry, biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - PalettedContainer.Strategy.SECTION_BIOMES - ); + PalettedContainer.Strategy.SECTION_BIOMES); final Palette> biomePalette; if (bitsPerEntry == 0) { @@ -601,12 +627,11 @@ public static PalettedContainer> getBiomePalettedContainer( int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes final int arrayLength = MathMan.longArrayLength(bitsPerEntryNonZero, 64); - - BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) : new SimpleBitStorage( - bitsPerEntry, - 64, - new long[arrayLength] - ); + BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) + : new SimpleBitStorage( + bitsPerEntry, + 64, + new long[arrayLength]); try { Object data = dataConstructor.newInstance(configuration, bitStorage, biomePalette); @@ -672,11 +697,13 @@ static List getEntities(LevelChunk chunk) { if (PaperLib.isPaper()) { return Optional.ofNullable(chunk.level .moonrise$getEntityLookup() - .getChunk(chunk.locX, chunk.locZ)).map(ChunkEntitySlices::getAllEntities).orElse(Collections.emptyList()); + .getChunk(chunk.locX, chunk.locZ)).map(ChunkEntitySlices::getAllEntities) + .orElse(Collections.emptyList()); } try { - //noinspection unchecked - return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos()); + // noinspection unchecked + return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))) + .getEntities(chunk.getPos()); } catch (IllegalAccessException e) { throw new RuntimeException("Failed to lookup entities [PAPER=false]", e); } diff --git a/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/regen/PaperweightRegen.java index a2efec8865..714400f9cd 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/regen/PaperweightRegen.java @@ -1,6 +1,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_6.regen; import com.fastasyncworldedit.bukkit.adapter.Regenerator; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; @@ -41,6 +42,8 @@ import java.nio.file.Path; import java.util.Map; import java.util.OptionalLong; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.function.BooleanSupplier; import java.util.function.Supplier; @@ -52,14 +55,13 @@ public class PaperweightRegen extends Regenerator { private static final Field paperConfigField; private static final Field generatorSettingBaseSupplierField; - static { try { serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField.setAccessible(true); Field tmpPaperConfigField; - try { //only present on paper + try { // only present on paper tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField.setAccessible(true); } catch (Exception e) { @@ -75,7 +77,7 @@ public class PaperweightRegen extends Regenerator { } } - //runtime + // runtime private ServerLevel originalServerWorld; private ServerLevel freshWorld; private LevelStorageSource.LevelStorageAccess session; @@ -86,8 +88,7 @@ public PaperweightRegen( World originalBukkitWorld, Region region, Extent target, - RegenOptions options - ) { + RegenOptions options) { super(originalBukkitWorld, region, target, options); } @@ -109,10 +110,10 @@ protected boolean prepare() { @Override protected boolean initNewWorld() throws Exception { - //world folder + // world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); - //prepare for world init (see upstream implementation for reference) + // prepare for world init (see upstream implementation for reference) org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment(); org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator(); LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir); @@ -132,21 +133,19 @@ protected boolean initNewWorld() throws Exception { originalWorldData.settings.difficulty(), originalWorldData.settings.allowCommands(), originalWorldData.settings.gameRules(), - originalWorldData.settings.getDataConfiguration() - ); + originalWorldData.settings.getDataConfiguration()); - PrimaryLevelData.SpecialWorldProperty specialWorldProperty = - originalWorldData.isFlatWorld() - ? PrimaryLevelData.SpecialWorldProperty.FLAT - : originalWorldData.isDebugWorld() - ? PrimaryLevelData.SpecialWorldProperty.DEBUG - : PrimaryLevelData.SpecialWorldProperty.NONE; - PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable()); + PrimaryLevelData.SpecialWorldProperty specialWorldProperty = originalWorldData.isFlatWorld() + ? PrimaryLevelData.SpecialWorldProperty.FLAT + : originalWorldData.isDebugWorld() + ? PrimaryLevelData.SpecialWorldProperty.DEBUG + : PrimaryLevelData.SpecialWorldProperty.NONE; + PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, + Lifecycle.stable()); BiomeProvider biomeProvider = getBiomeProvider(); - - //init world + // init world freshWorld = Fawe.instance().getQueueHandler().sync((Supplier) () -> new ServerLevel( server, server.executor, @@ -155,8 +154,7 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.dimension(), new LevelStem( originalServerWorld.dimensionTypeRegistration(), - originalServerWorld.getChunkSource().getGenerator() - ), + originalServerWorld.getChunkSource().getGenerator()), new RegenNoOpWorldLoadListener(), originalServerWorld.isDebug(), seed, @@ -165,13 +163,14 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.getRandomSequences(), environment, generator, - biomeProvider - ) { + biomeProvider) { - private final Holder singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess() - .lookupOrThrow(BIOME).asHolderIdMap().byIdOrThrow( - WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) - ) : null; + private final Holder singleBiome = options.hasBiomeType() + ? DedicatedServer.getServer().registryAccess() + .lookupOrThrow(BIOME).asHolderIdMap().byIdOrThrow( + WorldEditPlugin.getInstance().getBukkitImplAdapter() + .getInternalBiomeId(options.getBiomeType())) + : null; @Override public @Nonnull Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { @@ -185,8 +184,7 @@ protected boolean initNewWorld() throws Exception { public void save( final ProgressListener progressListener, final boolean flush, - final boolean savingDisabled - ) { + final boolean savingDisabled) { // noop, spigot } @@ -195,20 +193,58 @@ public void save( final ProgressListener progressListener, final boolean flush, final boolean savingDisabled, - final boolean close - ) { + final boolean close) { // noop, paper } }).get(); freshWorld.noSave = true; removeWorldFromWorldsMap(); - newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name + newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); // rename to original world name if (paperConfigField != null) { paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); } + + if (FoliaLibHolder.isFolia()) { + return initWorldForFolia(newWorldData); + } + return true; } + private boolean initWorldForFolia(PrimaryLevelData worldData) throws ExecutionException, InterruptedException { + MinecraftServer console = ((CraftServer) Bukkit.getServer()).getServer(); + + ChunkPos spawnChunk = new ChunkPos( + freshWorld.getChunkSource().randomState().sampler().findSpawnPosition()); + + setRandomSpawnSelection(spawnChunk); + + CompletableFuture initFuture = new CompletableFuture<>(); + + FoliaLibHolder.getScheduler().runAtLocation( + freshWorld.getWorld().getChunkAt(spawnChunk.x, spawnChunk.z).getBlock(0, 0, 0).getLocation(), + task -> { + try { + console.initWorld(freshWorld, worldData, worldData, worldData.worldGenOptions()); + initFuture.complete(true); + } catch (Exception e) { + initFuture.completeExceptionally(e); + } + }); + + return initFuture.get(); + } + + private void setRandomSpawnSelection(ChunkPos spawnChunk) { + try { + Field randomSpawnField = ServerLevel.class.getDeclaredField("randomSpawnSelection"); + randomSpawnField.setAccessible(true); + randomSpawnField.set(freshWorld, spawnChunk); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to set randomSpawnSelection for Folia world initialization", e); + } + } + @Override protected void cleanup() { try { @@ -216,7 +252,7 @@ protected void cleanup() { } catch (Exception ignored) { } - //shutdown chunk provider + // shutdown chunk provider try { Fawe.instance().getQueueHandler().sync(() -> { try { @@ -229,29 +265,35 @@ protected void cleanup() { } catch (Exception ignored) { } - //remove world from server + // remove world from server try { Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap); } catch (Exception ignored) { } - //delete directory + // delete directory try { SafeFiles.tryHardToDeleteDir(tempDir); } catch (Exception ignored) { } } + @Override + protected World getFreshWorld() { + return freshWorld != null ? freshWorld.getWorld() : null; + } + @Override protected IChunkCache initSourceQueueCache() { return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } - //util + // util @SuppressWarnings("unchecked") private void removeWorldFromWorldsMap() { try { - Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + Map map = (Map) serverWorldsField + .get(Bukkit.getServer()); map.remove("faweregentempworld"); } catch (IllegalAccessException e) { throw new RuntimeException(e); @@ -278,8 +320,7 @@ public void updateSpawnPos(@Nonnull ChunkPos spawnPos) { @Override public void onStatusChange( final @Nonnull ChunkPos pos, - @org.jetbrains.annotations.Nullable final net.minecraft.world.level.chunk.status.ChunkStatus status - ) { + @org.jetbrains.annotations.Nullable final net.minecraft.world.level.chunk.status.ChunkStatus status) { } diff --git a/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightFaweAdapter.java index cab9c64b8d..2c039aeddc 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightFaweAdapter.java @@ -10,6 +10,7 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; @@ -21,6 +22,7 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_9.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_9.regen.PaperweightRegen; @@ -131,6 +133,7 @@ import java.util.Objects; import java.util.OptionalInt; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -144,12 +147,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter COMPONENTS_CODEC = DataComponentPatch.CODEC.optionalFieldOf( - "components", DataComponentPatch.EMPTY - ).codec(); + "components", DataComponentPatch.EMPTY).codec(); static { try { - CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave"); + CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class + .getDeclaredMethod("wasAccessibleSinceLastSave"); } catch (NoSuchMethodException ignored) { // may not be present in newer paper versions } } @@ -163,34 +166,36 @@ public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodExcepti public Function blockEntityToCompoundTag() { return blockEntity -> FaweCompoundTag.of( () -> { - // TODO (VI/O) it might be possible to have a custom ValueOutput that writes into a LinCompoundTag.Builder - // directly + // TODO (VI/O) it might be possible to have a custom ValueOutput that writes + // into a LinCompoundTag.Builder + // directly TagValueOutput output = createOutput(); blockEntity.saveWithId(output); return (LinCompoundTag) toNativeLin(output.buildResult()); - } - ); + }); } public static Property adaptProperty(net.minecraft.world.level.block.state.properties.Property property) { return switch (property) { case net.minecraft.world.level.block.state.properties.BooleanProperty booleanProperty -> - new BooleanProperty(booleanProperty.getName(), ImmutableList.copyOf(booleanProperty.getPossibleValues())); + new BooleanProperty(booleanProperty.getName(), + ImmutableList.copyOf(booleanProperty.getPossibleValues())); case net.minecraft.world.level.block.state.properties.IntegerProperty integerProperty -> - new IntegerProperty(integerProperty.getName(), ImmutableList.copyOf(integerProperty.getPossibleValues())); + new IntegerProperty(integerProperty.getName(), + ImmutableList.copyOf(integerProperty.getPossibleValues())); case net.minecraft.world.level.block.state.properties.EnumProperty enumProperty -> { if (enumProperty.getValueClass() == net.minecraft.core.Direction.class) { yield new DirectionalProperty(enumProperty.getName(), enumProperty.getPossibleValues().stream() .map(StringRepresentable::getSerializedName) .map(s -> s.toUpperCase(Locale.ROOT)) .map(Direction::valueOf) - .toList() - ); + .toList()); } yield new EnumProperty(enumProperty.getName(), enumProperty.getPossibleValues().stream() .map(StringRepresentable::getSerializedName).collect(Collectors.toCollection(ArrayList::new))); } - default -> throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + property.getClass().getSimpleName()); + default -> throw new IllegalArgumentException( + "FastAsyncWorldEdit needs an update to support " + property.getClass().getSimpleName()); }; } @@ -271,7 +276,8 @@ public BlockMaterial getMaterial(BlockType blockType) { @Override public synchronized BlockMaterial getMaterial(BlockState state) { - net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState(); + net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit + .createBlockData(state.getAsString())).getState(); return new PaperweightBlockMaterial(blockState.getBlock(), blockState); } @@ -336,8 +342,7 @@ public BaseBlock getFullBlock(final Location location) { SideEffect.HISTORY, SideEffect.HEIGHTMAPS, SideEffect.LIGHTING, - SideEffect.NEIGHBORS - ); + SideEffect.NEIGHBORS); @Override public Set getSupportedSideEffects() { @@ -363,7 +368,7 @@ public BaseEntity getEntity(org.bukkit.entity.Entity entity) { if (!readEntityIntoTag(mcEntity, minecraftTag)) { return null; } - //add Id for AbstractChangeSet to work + // add Id for AbstractChangeSet to work final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag); final Map> tags = NbtUtils.getLinCompoundTagValues(tag); tags.put("Id", LinStringTag.of(id)); @@ -420,8 +425,7 @@ public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockSt return (char) ibdToOrdinal[id]; } catch (ArrayIndexOutOfBoundsException e1) { LOGGER.error("Attempted to convert {} with ID {} to char. ibdToOrdinal length: {}. Defaulting to air!", - blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1 - ); + blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToOrdinal.length, e1); return BlockTypesCache.ReservedIDs.AIR; } } @@ -494,18 +498,22 @@ public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockSt @Override public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { ServerLevel nmsWorld = getServerLevel(world); - ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); + ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), + chunkPacket.getChunkZ()); if (map != null && wasAccessibleSinceLastSave(map)) { boolean flag = false; // PlayerChunk.d players = map.players; - Stream stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag) - */ Stream.empty(); + Stream stream = /* + * players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), + * flag) + */ Stream.empty(); ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle(); stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer) .forEach(entityPlayer -> { synchronized (chunkPacket) { - ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket.getNativePacket(); + ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket + .getNativePacket(); if (nmsPacket == null) { nmsPacket = mapUtil.create(this, chunkPacket); chunkPacket.setNativePacket(nmsPacket); @@ -532,8 +540,7 @@ public boolean canPlaceAt(org.bukkit.World world, BlockVector3 blockVector3, Blo net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); return blockState1.hasPostProcess( getServerLevel(world), - new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z()) - ); + new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z())); } @Override @@ -541,10 +548,8 @@ public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) { final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess(); ItemStack stack = new ItemStack( registryAccess.lookupOrThrow(Registries.ITEM).getValueOrThrow(ResourceKey.create( - Registries.ITEM, ResourceLocation.parse(baseItemStack.getType().id()) - )), - baseItemStack.getAmount() - ); + Registries.ITEM, ResourceLocation.parse(baseItemStack.getType().id()))), + baseItemStack.getAmount()); final CompoundTag nbt = (net.minecraft.nbt.CompoundTag) fromNativeLin(baseItemStack.getNbt()); if (nbt != null) { final DataComponentPatch patch = COMPONENTS_CODEC @@ -572,8 +577,22 @@ protected void postCaptureBlockStates(final ServerLevel serverLevel) { serverLevel.captureTreeGeneration = false; serverLevel.capturedBlockStates.clear(); } + + private T syncRegion(World world, BlockVector3 pt, java.util.function.Supplier supplier) { + if (FoliaLibHolder.isFolia()) { + Location location = new Location(world, pt.x(), pt.y(), pt.z()); + CompletableFuture future = new CompletableFuture<>(); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> future.complete(supplier.get())); + return future.join(); + } + return TaskManager.taskManager().sync(supplier); + } + @Override - public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, BlockVector3 pt) { + public boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession editSession, + BlockVector3 pt) { ServerLevel serverLevel = getServerLevel(world); ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); @@ -583,15 +602,14 @@ public boolean generateFeature(ConfiguredFeatureType feature, World world, EditS .getValue(ResourceLocation.tryParse(feature.id())); FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { if (!configuredFeature.place( populator, generator, serverLevel.random, - new BlockPos(pt.x(), pt.y(), pt.z()) - )) { + new BlockPos(pt.x(), pt.y(), pt.z()))) { return null; } List placedBlocks = new ArrayList<>(populator.getSnapshotBlocks()); @@ -620,7 +638,7 @@ public boolean generateStructure(StructureType type, World world, EditSession ed TransformerGeneratorAccess access = new TransformerGeneratorAccess(); FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); access.setDelegate(populator); - List placed = TaskManager.taskManager().sync(() -> { + List placed = syncRegion(world, pt, () -> { preCaptureStates(serverLevel); try { StructureStart structureStart = structure.generate( @@ -635,20 +653,17 @@ public boolean generateStructure(StructureType type, World world, EditSession ed chunkPos, 0, populator, - biome -> true - ); + biome -> true); if (!structureStart.isValid()) { return null; } else { BoundingBox boundingBox = structureStart.getBoundingBox(); ChunkPos min = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.minX()), - SectionPos.blockToSectionCoord(boundingBox.minZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.minZ())); ChunkPos max = new ChunkPos( SectionPos.blockToSectionCoord(boundingBox.maxX()), - SectionPos.blockToSectionCoord(boundingBox.maxZ()) - ); + SectionPos.blockToSectionCoord(boundingBox.maxZ())); ChunkPos.rangeClosed(min, max).forEach((chunkPosx) -> structureStart.placeInChunk( access, serverLevel.structureManager(), @@ -659,9 +674,8 @@ public boolean generateStructure(StructureType type, World world, EditSession ed serverLevel.getMinY(), chunkPosx.getMinBlockZ(), chunkPosx.getMaxBlockX(), - serverLevel.getMaxY(), chunkPosx.getMaxBlockZ() - ), chunkPosx - )); + serverLevel.getMaxY(), chunkPosx.getMaxBlockZ()), + chunkPosx)); List placedBlocks = new ArrayList<>(populator.getSnapshotBlocks()); placedBlocks.addAll(serverLevel.capturedBlockStates.values()); return placedBlocks; @@ -677,8 +691,7 @@ public boolean generateStructure(StructureType type, World world, EditSession ed private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, - final List placed - ) { + final List placed) { if (placed == null || placed.isEmpty()) { return false; } @@ -688,13 +701,15 @@ private boolean placeFeatureIntoSession( continue; } BlockPos pos = craftBlockState.getPosition(); - editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), BukkitAdapter.adapt(craftBlockState.getBlockData())); + editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), + BukkitAdapter.adapt(craftBlockState.getBlockData())); BlockEntity blockEntity = populator.getBlockEntity(pos); if (blockEntity != null) { // TODO (VI/O) TagValueOutput output = createOutput(); blockEntity.saveWithId(output); - editSession.setTile(pos.getX(), pos.getY(), pos.getZ(), (com.sk89q.jnbt.CompoundTag) toNative(output.buildResult())); + editSession.setTile(pos.getX(), pos.getY(), pos.getZ(), + (com.sk89q.jnbt.CompoundTag) toNative(output.buildResult())); } } return true; @@ -706,7 +721,8 @@ public void setupFeatures() { // All these features should be the "face" selected Set face_features = Arrays - .stream(new Class[]{AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, VegetationFeatures.class}) + .stream(new Class[] { AquaticFeatures.class, PileFeatures.class, TreeFeatures.class, + VegetationFeatures.class }) .flatMap(c -> Arrays.stream(c.getFields())) .filter(f -> { int modifiers = f.getModifiers(); @@ -765,16 +781,15 @@ protected ServerLevel getServerLevel(final World world) { public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess(); final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); - // We should be fine to perform this later as we're using a deep-copied itemstack (above) + // We should be fine to perform this later as we're using a deep-copied + // itemstack (above) final Supplier tag = () -> COMPONENTS_CODEC.encodeStart( registryAccess.createSerializationContext(NbtOps.INSTANCE), - nmsStack.getComponentsPatch() - ).getOrThrow(); + nmsStack.getComponentsPatch()).getOrThrow(); return new BaseItemStack( BukkitAdapter.asItemType(itemStack.getType()), LazyReference.from(() -> (LinCompoundTag) toNativeLin(tag.get())), - itemStack.getAmount() - ); + itemStack.getAmount()); } @Override @@ -788,7 +803,8 @@ public net.minecraft.nbt.Tag fromNative(Tag foreign) { } @Override - public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception { + public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) + throws Exception { return new PaperweightRegen(bukkitWorld, region, target, options).regenerate(); } diff --git a/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightFaweWorldNativeAccess.java index e778eecae8..47148da7c0 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightFaweWorldNativeAccess.java @@ -22,6 +22,7 @@ import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.redstone.ExperimentalRedstoneUtils; import net.minecraft.world.level.storage.ValueInput; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.event.block.BlockPhysicsEvent; @@ -62,7 +63,7 @@ public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAd this.level = level; // Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging. // - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway. - this.lastTick = new AtomicInteger(MinecraftServer.currentTick); + this.lastTick = new AtomicInteger(getCurrentTick()); } private Level getLevel() { @@ -98,7 +99,7 @@ public synchronized net.minecraft.world.level.block.state.BlockState setBlockSta LevelChunk levelChunk, BlockPos blockPos, net.minecraft.world.level.block.state.BlockState blockState ) { - int currentTick = MinecraftServer.currentTick; + int currentTick = getCurrentTick(); if (Fawe.isMainThread()) { return levelChunk.setBlockState(blockPos, blockState, this.sideEffectSet.shouldApply(SideEffect.UPDATE) ? 0 : 512 @@ -296,4 +297,16 @@ private record CachedChange( } + private int getCurrentTick() { + try { + return MinecraftServer.currentTick; + } catch (NoSuchFieldError e) { + try { + return Bukkit.getCurrentTick(); + } catch (Exception ex) { + return 0; + } + } + } + } diff --git a/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightGetBlocks.java index 84e6d721d1..93dc0c7f53 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightGetBlocks.java @@ -11,6 +11,7 @@ import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; @@ -23,6 +24,7 @@ import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockTypesCache; import io.papermc.lib.PaperLib; import io.papermc.paper.event.block.BeaconDeactivatedEvent; @@ -58,6 +60,8 @@ import net.minecraft.world.level.storage.TagValueOutput; import net.minecraft.world.level.storage.ValueInput; import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.World; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.CraftBlock; @@ -97,7 +101,8 @@ public class PaperweightGetBlocks extends AbstractBukkitGetBlocks posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); + private static final Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), + v.getZ()); public static final Function NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin .getInstance() .getBukkitImplAdapter()).blockEntityToCompoundTag(); @@ -170,8 +175,9 @@ public BiomeType getBiomeType(int x, int y, int z) { @Override public void removeSectionLighting(int layer, boolean sky) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK) + .getDataLayerData( + sectionPos); if (dataLayer != null) { lightUpdate = true; synchronized (dataLayer) { @@ -198,9 +204,8 @@ public void removeSectionLighting(int layer, boolean sky) { @Override 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))); + BlockEntity blockEntity = getChunk() + .getBlockEntity(new BlockPos((x & 15) + (chunkX << 4), y, (z & 15) + (chunkZ << 4))); if (blockEntity == null) { return null; } @@ -223,19 +228,19 @@ public int getSkyLight(int x, int y, int z) { int alayer = layer - getMinSectionPosition(); if (skyLight[alayer] == null) { SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = - serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getDataLayerData(sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY) + .getDataLayerData(sectionPos); // If the server hasn't generated the section's NibbleArray yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( LightLayer.BLOCK, sectionPos, - dataLayer - ); + dataLayer); } skyLight[alayer] = dataLayer; } @@ -257,12 +262,13 @@ public int getEmittedLight(int x, int y, int z) { // If the server hasn't generated the section's DataLayer yet, it will be null if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be + // created before lighting is fixed anyway. Arrays.fill(LAYER_COUNT, (byte) 15); dataLayer = new DataLayer(LAYER_COUNT); - ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, sectionPos, - dataLayer - ); + ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, + sectionPos, + dataLayer); } blockLight[alayer] = dataLayer; } @@ -336,8 +342,7 @@ protected > T internalCall( Runnable finalizer, int copyKey, LevelChunk nmsChunk, - ServerLevel nmsWorld - ) throws Exception { + ServerLevel nmsWorld) throws Exception { PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null; if (createCopy) { if (copies.containsKey(copyKey)) { @@ -367,7 +372,18 @@ protected > T internalCall( beacons = new ArrayList<>(); } beacons.add(tile); - PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + if (FoliaLibHolder.isFolia()) { + Location location = new Location( + nmsWorld.getWorld(), + tile.getBlockPos().getX(), + tile.getBlockPos().getY(), + tile.getBlockPos().getZ()); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk)); + } else { + PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + } continue; } nmsChunk.removeBlockEntity(tile.getBlockPos()); @@ -404,33 +420,32 @@ protected > T internalCall( } if (existingSection == null) { - PalettedContainer> biomeData = PaperweightPlatformAdapter.getBiomePalettedContainer( - biomes[setSectionIndex], - biomeHolderIdMap - ); + PalettedContainer> biomeData = PaperweightPlatformAdapter + .getBiomePalettedContainer( + biomes[setSectionIndex], + biomeHolderIdMap); LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, new char[4096], adapter, serverLevel.registryAccess(), - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { - updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex); + getSectionIndex)) { + updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], + getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; if (existingSection == null) { - LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, + chunkZ, + getSectionIndex); continue; } } @@ -438,8 +453,7 @@ protected > T internalCall( PalettedContainer> paletteBiomes = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); if (paletteBiomes != null) { PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes); } @@ -451,13 +465,16 @@ protected > T internalCall( bitMask |= 1 << getSectionIndex; - // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to - // this chunk GET when #updateGet is called. Future dords, please listen this time. + // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in + // order to write changes to + // this chunk GET when #updateGet is called. Future dords, please listen this + // time. char[] tmp = set.load(layerNo); char[] setArr = new char[tmp.length]; System.arraycopy(tmp, 0, setArr, 0, tmp.length); - // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was + // synchronise on internal section to avoid circular locking with a continuing + // edit if the chunk was // submitted to keep loaded internal chunks to queue target size. synchronized (super.sectionLocks[getSectionIndex]) { @@ -480,38 +497,37 @@ protected > T internalCall( if (existingSection == null) { - PalettedContainer> biomeData = biomes == null ? - serverLevel.palettedContainerFactory().createForBiomes() : - PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap); + PalettedContainer> biomeData = biomes == null + ? serverLevel.palettedContainerFactory().createForBiomes() + : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], + biomeHolderIdMap); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, setArr, adapter, serverLevel.registryAccess(), - biomeData - ); + biomeData); if (PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, - getSectionIndex - )) { + getSectionIndex)) { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; if (existingSection == null) { LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); continue; } } } - //ensure that the server doesn't try to tick the chunksection while we're editing it. (Again) + // ensure that the server doesn't try to tick the chunksection while we're + // editing it. (Again) PaperweightPlatformAdapter.clearCounts(existingSection); DelegateSemaphore lock = PaperweightPlatformAdapter.applyLock(existingSection); @@ -530,11 +546,12 @@ protected > T internalCall( this.reset(); } else if (!Arrays.equals( update(getSectionIndex, new char[4096], true), - load(layerNo) - )) { + load(layerNo))) { this.reset(layerNo); - /*} else if (lock.isModified()) { - this.reset(layerNo);*/ + /* + * } else if (lock.isModified()) { + * this.reset(layerNo); + */ } } finally { sectionLock.writeLock().unlock(); @@ -543,8 +560,7 @@ protected > T internalCall( PalettedContainer> biomeData = setBiomesToPalettedContainer( biomes, setSectionIndex, - existingSection.getBiomes() - ); + existingSection.getBiomes()); newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, @@ -552,19 +568,17 @@ protected > T internalCall( setArr, adapter, serverLevel.registryAccess(), - biomeData != null ? biomeData : (PalettedContainer>) existingSection.getBiomes() - ); + biomeData != null ? biomeData + : (PalettedContainer>) existingSection.getBiomes()); if (!PaperweightPlatformAdapter.setSectionAtomic( nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, existingSection, newSection, - getSectionIndex - )) { + getSectionIndex)) { LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - getSectionIndex - ); + getSectionIndex); } else { updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); } @@ -579,13 +593,11 @@ protected > T internalCall( PaperweightGetBlocks.this.setLightingToGet( set.getLight(), set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); + set.getMaxSectionPosition()); PaperweightGetBlocks.this.setSkyLightingToGet( set.getSkyLight(), set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); + set.getMaxSectionPosition()); List syncTasks = new ArrayList<>(); @@ -599,7 +611,8 @@ protected > T internalCall( syncTasks.add(() -> { for (BlockEntity beacon : finalBeacons) { - BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), SoundEvents.BEACON_DEACTIVATE); + BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), + SoundEvents.BEACON_DEACTIVATE); new BeaconDeactivatedEvent(CraftBlock.at(beacon.getLevel(), beacon.getBlockPos())).callEvent(); } }); @@ -672,17 +685,33 @@ protected > T internalCall( entity.load(input); entity.absSnapTo(x, y, z, yaw, pitch); entity.setUUID(NbtUtils.uuid(nativeTag)); - if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { - LOGGER.warn( - "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", - id, - nmsWorld.getWorld().getName(), - x, - y, - z - ); - // Unsuccessful create should not be saved to history - iterator.remove(); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } + }); + } else { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z); + // Unsuccessful create should not be saved to history + iterator.remove(); + } } } } @@ -703,20 +732,42 @@ protected > T internalCall( final int z = blockHash.z() + bz; final BlockPos pos = new BlockPos(x, y, z); - synchronized (nmsWorld) { - BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); - if (tileEntity == null || tileEntity.isRemoved()) { - nmsWorld.removeBlockEntity(pos); - tileEntity = nmsWorld.getBlockEntity(pos); - } - if (tileEntity != null) { - 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)); - // TODO (VI/O) - ValueInput input = createInput(tag); - tileEntity.loadWithComponents(input); + if (FoliaLibHolder.isFolia()) { + Location location = new Location(nmsWorld.getWorld(), x, y, z); + FoliaLibHolder.getScheduler().runAtLocation(location, scheduledTask -> { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + // TODO (VI/O) + ValueInput input = createInput(tag); + tileEntity.loadWithComponents(input); + } + } + }); + } else { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + 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)); + // TODO (VI/O) + ValueInput input = createInput(tag); + tileEntity.loadWithComponents(input); + } } } } @@ -737,7 +788,8 @@ protected > T internalCall( // send to player if (!set .getSideEffectSet() - .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) { + .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING + || finalMask == 0 && biomes != null) { this.send(); } if (finalizer != null) { @@ -754,8 +806,7 @@ private void updateGet( LevelChunkSection[] chunkSections, LevelChunkSection section, char[] arr, - int layer - ) { + int layer) { try { sectionLock.writeLock().lock(); if (this.getChunk() != nmsChunk) { @@ -769,8 +820,9 @@ private void updateGet( System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); } if (this.sections[layer] != section) { - // Not sure why it's funky, but it's what I did in commit fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords - this.sections[layer] = new LevelChunkSection[]{section}.clone()[0]; + // Not sure why it's funky, but it's what I did in commit + // fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords + this.sections[layer] = new LevelChunkSection[] { section }.clone()[0]; } } finally { sectionLock.writeLock().unlock(); @@ -786,14 +838,19 @@ public void send() { } /** - * Update a given (nullable) data array to the current data stored in the server's chunk, associated with this - * {@link PaperweightPlatformAdapter} instance. Not synchronised to the {@link PaperweightPlatformAdapter} instance as synchronisation - * is handled where necessary in the method, and should otherwise be handled correctly by this method's caller. + * Update a given (nullable) data array to the current data stored in the + * server's chunk, associated with this + * {@link PaperweightPlatformAdapter} instance. Not synchronised to the + * {@link PaperweightPlatformAdapter} instance as synchronisation + * is handled where necessary in the method, and should otherwise be handled + * correctly by this method's caller. * - * @param layer layer index (0 may denote a negative layer in the world, e.g. at y=-32) + * @param layer layer index (0 may denote a negative layer in the world, + * e.g. at y=-32) * @param data array to be updated/filled with data or null * @param aggressive if the cached section array should be re-acquired. - * @return the given array to be filled with data, or a new array if null is given. + * @return the given array to be filled with data, or a new array if null is + * given. */ @Override @SuppressWarnings("unchecked") @@ -823,7 +880,8 @@ public char[] update(int layer, char[] data, boolean aggressive) { return data; } - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get(dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get(dataObject); final int bitsPerEntry = bits.getBits(); final long[] blockStates = bits.getRaw(); @@ -903,10 +961,10 @@ public LevelChunk getChunk() { } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e); throw new FaweException( - TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), + TextComponent + .of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), FaweException.Type.OTHER, - false - ); + false); } } } @@ -914,14 +972,16 @@ public LevelChunk getChunk() { return levelChunk; } - private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, int maxSectionPosition) { + private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, + int maxSectionPosition) { for (int Y = 0; Y <= maxSectionPosition - minSectionPosition; Y++) { if (light[Y] == null) { continue; } SectionPos sectionPos = SectionPos.of(levelChunk.getPos(), Y + minSectionPosition); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer).getDataLayerData( - sectionPos); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer) + .getDataLayerData( + sectionPos); if (dataLayer == null) { byte[] LAYER_COUNT = new byte[2048]; Arrays.fill(LAYER_COUNT, lightLayer == LightLayer.SKY ? (byte) 15 : (byte) 0); @@ -929,8 +989,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( lightLayer, sectionPos, - dataLayer - ); + dataLayer); } synchronized (dataLayer) { for (int x = 0; x < 16; x++) { @@ -950,8 +1009,7 @@ private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSecti private PalettedContainer> setBiomesToPalettedContainer( final BiomeType[][] biomes, final int sectionIndex, - final PalettedContainerRO> data - ) { + final PalettedContainerRO> data) { BiomeType[] sectionBiomes; if (biomes == null || (sectionBiomes = biomes[sectionIndex]) == null) { return null; @@ -968,8 +1026,7 @@ private PalettedContainer> setBiomesToPalettedContainer( x, y, z, - biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType)) - ); + biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType))); } } } @@ -1025,8 +1082,9 @@ public boolean trim(boolean aggressive) { final PalettedContainer blocksExisting = existing.getStates(); final Object dataObject = PaperweightPlatformAdapter.fieldData.get(blocksExisting); - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get( - dataObject); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette + .get( + dataObject); int paletteSize; if (palette instanceof LinearPalette || palette instanceof HashMapPalette) { @@ -1036,7 +1094,8 @@ public boolean trim(boolean aggressive) { continue; } if (paletteSize == 1) { - //If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks. + // If the cached palette size is 1 then no blocks can have been changed i.e. do + // not need to update these chunks. continue; } super.trim(false, i); diff --git a/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightPlatformAdapter.java index e1fca93ca2..691b4131a9 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightPlatformAdapter.java @@ -9,6 +9,7 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.serialization.DataResult; @@ -21,6 +22,7 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import io.papermc.lib.PaperLib; +import io.papermc.paper.util.MCUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.IdMap; @@ -70,6 +72,7 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -126,23 +129,25 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c")); fieldPalette.setAccessible(true); - //noinspection JavaLangInvokeHandleSignature - method is obfuscated - palettedContainerUnpackSpigot = PaperLib.isPaper() ? null : lookup.findStatic( - PalettedContainer.class, - "a", // unpack - MethodType.methodType(DataResult.class, Strategy.class, PalettedContainerRO.PackedData.class) - ); + // noinspection JavaLangInvokeHandleSignature - method is obfuscated + palettedContainerUnpackSpigot = PaperLib.isPaper() ? null + : lookup.findStatic( + PalettedContainer.class, + "a", // unpack + MethodType.methodType(DataResult.class, Strategy.class, + PalettedContainerRO.PackedData.class)); fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName( "tickingFluidCount", - "g" - )); + "g")); fieldTickingFluidCount.setAccessible(true); - fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); + fieldTickingBlockCount = LevelChunkSection.class + .getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); fieldTickingBlockCount.setAccessible(true); Field tmpFieldBiomes; try { - // Seems it's sometimes biomes and sometimes "i". Idk this is just easier than having to try to deal with it + // Seems it's sometimes biomes and sometimes "i". Idk this is just easier than + // having to try to deal with it tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("biomes"); // apparently unobf } catch (NoSuchFieldException ignored) { tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("i"); // apparently obf @@ -153,18 +158,19 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod( Refraction.pickName( "getVisibleChunkIfPresent", - "b" - ), long.class - ); + "b"), + long.class); getVisibleChunkIfPresent.setAccessible(true); methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent); if (!PaperLib.isPaper()) { - fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "d")); + fieldThreadingDetector = PalettedContainer.class + .getDeclaredField(Refraction.pickName("threadingDetector", "d")); fieldThreadingDetector.setAccessible(true); fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); fieldLock.setAccessible(true); - SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "M")); + SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class + .getDeclaredField(Refraction.pickName("entityManager", "M")); SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true); } else { // in paper, the used methods are synchronized properly @@ -175,17 +181,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( Refraction.pickName("removeGameEventListener", "a"), BlockEntity.class, - ServerLevel.class - ); + ServerLevel.class); removeGameEventListener.setAccessible(true); methodRemoveGameEventListener = lookup.unreflect(removeGameEventListener); Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod( Refraction.pickName( "removeBlockEntityTicker", - "k" - ), BlockPos.class - ); + "k"), + BlockPos.class); removeBlockEntityTicker.setAccessible(true); methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker); @@ -194,8 +198,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { Method palettedContainerGet = PalettedContainer.class.getDeclaredMethod( Refraction.pickName("get", "a"), - int.class - ); + int.class); palettedContainerGet.setAccessible(true); PALETTED_CONTAINER_GET = lookup.unreflect(palettedContainerGet); } catch (RuntimeException | Error e) { @@ -208,24 +211,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { public static TagValueOutput createOutput() { return TagValueOutput.createWithContext( ProblemReporter.DISCARDING, - DedicatedServer.getServer().registryAccess() - ); + DedicatedServer.getServer().registryAccess()); } public static TagValueOutput createOutput(CompoundTag compoundTag) { return TagValueOutput.createWrappingWithContext( ProblemReporter.DISCARDING, DedicatedServer.getServer().registryAccess(), - compoundTag - ); + compoundTag); } public static ValueInput createInput(CompoundTag nativeTag) { return TagValueInput.create( ProblemReporter.DISCARDING, DedicatedServer.getServer().registryAccess(), - nativeTag - ); + nativeTag); } static boolean setSectionAtomic( @@ -234,14 +234,13 @@ static boolean setSectionAtomic( LevelChunkSection[] sections, LevelChunkSection expected, LevelChunkSection value, - int layer - ) { + int layer) { return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer); } // There is no point in having a functional semaphore for paper servers. - private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = - ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null)); + private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = ThreadLocal + .withInitial(() -> new DelegateSemaphore(1, null)); static DelegateSemaphore applyLock(LevelChunkSection section) { if (PaperLib.isPaper()) { @@ -296,11 +295,11 @@ public static CompletableFuture ensureLoaded(ServerLevel serverLevel "Unexpected error when getting completed future at chunk {},{}. Returning to default.", chunkX, chunkZ, - e - ); + e); } } - return CompletableFuture.supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); + return CompletableFuture + .supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); } private static LevelChunk toLevelChunk(Chunk chunk) { @@ -337,6 +336,14 @@ private static LevelChunk toLevelChunk(Chunk chunk) { } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { + if (FoliaLibHolder.isFolia()) { + try { + serverLevel.getChunkSource().addTicketWithRadius(ChunkHolderManager.UNLOAD_COOLDOWN, + new ChunkPos(chunkX, chunkZ), 0); + } catch (Exception ignored) { + } + return; + } // Ensure chunk is definitely loaded before applying a ticket io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel .getChunkSource() @@ -373,7 +380,7 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int if (lockHolder.chunkLock == null) { return; } - MinecraftServer.getServer().execute(() -> { + if (FoliaLibHolder.isFolia()) { try { ChunkPos pos = levelChunk.getPos(); ClientboundLevelChunkWithLightPacket packet; @@ -391,14 +398,39 @@ public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int levelChunk, nmsWorld.getLightEngine(), null, - null - ); + null); } nearbyPlayers(nmsWorld, pos).forEach(p -> p.connection.send(packet)); } finally { NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); } - }); + } else { + MinecraftServer.getServer().execute(() -> { + try { + ChunkPos pos = levelChunk.getPos(); + ClientboundLevelChunkWithLightPacket packet; + if (PaperLib.isPaper()) { + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null, + false // last false is to not bother with x-ray + ); + } else { + // deprecated on paper - deprecation suppressed + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getLightEngine(), + null, + null); + } + nearbyPlayers(nmsWorld, pos).forEach(p -> p.connection.send(packet)); + } finally { + NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); + } + }); + } } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { @@ -406,15 +438,14 @@ private static List nearbyPlayers(ServerLevel serverLevel, ChunkPo } /* - NMS conversion + * NMS conversion */ public static LevelChunkSection newChunkSection( final int layer, final char[] blocks, CachedBukkitAdapter adapter, RegistryAccess registryAccess, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { return newChunkSection(layer, null, blocks, adapter, registryAccess, biomes); } @@ -424,8 +455,7 @@ public static LevelChunkSection newChunkSection( char[] set, CachedBukkitAdapter adapter, RegistryAccess registryAccess, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { if (set == null) { return newChunkSection(registryAccess, biomes); } @@ -476,14 +506,15 @@ public static LevelChunkSection newChunkSection( // Create palette with data var strategy = Strategy.createForBlockStates(Block.BLOCK_STATE_REGISTRY); - var packedData = new PalettedContainerRO.PackedData<>(palette, Optional.of(LongStream.of(bits)), bitsPerEntry); + var packedData = new PalettedContainerRO.PackedData<>(palette, Optional.of(LongStream.of(bits)), + bitsPerEntry); DataResult> result; if (PaperLib.isPaper()) { result = PalettedContainer.unpack(strategy, packedData, Blocks.AIR.defaultBlockState(), null); } else { - //noinspection unchecked - result = (DataResult>) - palettedContainerUnpackSpigot.invokeExact(strategy, packedData); + // noinspection unchecked + result = (DataResult>) palettedContainerUnpackSpigot + .invokeExact(strategy, packedData); } if (biomes == null) { biomes = PalettedContainerFactory.create(registryAccess).createForBiomes(); @@ -502,8 +533,7 @@ public static LevelChunkSection newChunkSection( @SuppressWarnings("deprecation") // Only deprecated in paper private static LevelChunkSection newChunkSection( RegistryAccess registryAccess, - @Nullable PalettedContainer> biomes - ) { + @Nullable PalettedContainer> biomes) { PalettedContainerFactory factory = PalettedContainerFactory.create(registryAccess); if (biomes == null) { return new LevelChunkSection(factory); @@ -520,17 +550,18 @@ public static void setBiomesToChunkSection(LevelChunkSection section, PalettedCo } /** - * Create a new {@link PalettedContainer}. Should only be used if no biome container existed beforehand. + * Create a new {@link PalettedContainer}. Should only be used if no + * biome container existed beforehand. */ public static PalettedContainer> getBiomePalettedContainer( BiomeType[] biomes, - IdMap> biomeRegistry - ) { + IdMap> biomeRegistry) { if (biomes == null) { return null; } BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - // Don't stream this as typically will see 1-4 biomes; stream overhead is large for the small length + // Don't stream this as typically will see 1-4 biomes; stream overhead is large + // for the small length final List> palette = new ArrayList<>(); for (BiomeType biomeType : new LinkedList<>(Set.of(biomes))) { if (biomeType == null) { @@ -551,21 +582,19 @@ public static PalettedContainer> getBiomePalettedContainer( var strategy = Strategy.createForBiomes(biomeRegistry); var packedData = new PalettedContainerRO.PackedData<>( - palette, Optional.of(LongStream.of(new long[arrayLength])), bitsPerEntry - ); + palette, Optional.of(LongStream.of(new long[arrayLength])), bitsPerEntry); DataResult>> result; if (PaperLib.isPaper()) { result = PalettedContainer.unpack( strategy, packedData, biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), - null - ); + null); } else { try { - //noinspection unchecked - result = (DataResult>>) - palettedContainerUnpackSpigot.invokeExact(strategy, packedData); + // noinspection unchecked + result = (DataResult>>) palettedContainerUnpackSpigot + .invokeExact(strategy, packedData); } catch (Throwable e) { throw new RuntimeException("Failed to create biome palette for Spigot", e); } @@ -631,11 +660,13 @@ static List getEntities(LevelChunk chunk) { if (PaperLib.isPaper()) { return Optional.ofNullable(chunk.level .moonrise$getEntityLookup() - .getChunk(chunk.locX, chunk.locZ)).map(ChunkEntitySlices::getAllEntities).orElse(Collections.emptyList()); + .getChunk(chunk.locX, chunk.locZ)).map(ChunkEntitySlices::getAllEntities) + .orElse(Collections.emptyList()); } try { - //noinspection unchecked - return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos()); + // noinspection unchecked + return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))) + .getEntities(chunk.getPos()); } catch (IllegalAccessException e) { throw new RuntimeException("Failed to lookup entities [PAPER=false]", e); } diff --git a/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/regen/PaperweightRegen.java index e31a940e58..07a4a7e72d 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/regen/PaperweightRegen.java @@ -1,6 +1,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_9.regen; import com.fastasyncworldedit.bukkit.adapter.Regenerator; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; @@ -20,6 +21,7 @@ import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.ProgressListener; +import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelSettings; import net.minecraft.world.level.biome.Biome; @@ -39,6 +41,8 @@ import java.nio.file.Path; import java.util.Map; import java.util.OptionalLong; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.function.BooleanSupplier; import java.util.function.Supplier; @@ -50,14 +54,13 @@ public class PaperweightRegen extends Regenerator { private static final Field paperConfigField; private static final Field generatorSettingBaseSupplierField; - static { try { serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField.setAccessible(true); Field tmpPaperConfigField; - try { //only present on paper + try { // only present on paper tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField.setAccessible(true); } catch (Exception e) { @@ -73,7 +76,7 @@ public class PaperweightRegen extends Regenerator { } } - //runtime + // runtime private ServerLevel originalServerWorld; private ServerLevel freshWorld; private LevelStorageSource.LevelStorageAccess session; @@ -84,8 +87,7 @@ public PaperweightRegen( World originalBukkitWorld, Region region, Extent target, - RegenOptions options - ) { + RegenOptions options) { super(originalBukkitWorld, region, target, options); } @@ -107,10 +109,10 @@ protected boolean prepare() { @Override protected boolean initNewWorld() throws Exception { - //world folder + // world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); - //prepare for world init (see upstream implementation for reference) + // prepare for world init (see upstream implementation for reference) org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment(); org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator(); LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir); @@ -130,21 +132,19 @@ protected boolean initNewWorld() throws Exception { originalWorldData.settings.difficulty(), originalWorldData.settings.allowCommands(), originalWorldData.settings.gameRules(), - originalWorldData.settings.getDataConfiguration() - ); + originalWorldData.settings.getDataConfiguration()); - PrimaryLevelData.SpecialWorldProperty specialWorldProperty = - originalWorldData.isFlatWorld() - ? PrimaryLevelData.SpecialWorldProperty.FLAT - : originalWorldData.isDebugWorld() - ? PrimaryLevelData.SpecialWorldProperty.DEBUG - : PrimaryLevelData.SpecialWorldProperty.NONE; - PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable()); + PrimaryLevelData.SpecialWorldProperty specialWorldProperty = originalWorldData.isFlatWorld() + ? PrimaryLevelData.SpecialWorldProperty.FLAT + : originalWorldData.isDebugWorld() + ? PrimaryLevelData.SpecialWorldProperty.DEBUG + : PrimaryLevelData.SpecialWorldProperty.NONE; + PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, + Lifecycle.stable()); BiomeProvider biomeProvider = getBiomeProvider(); - - //init world + // init world freshWorld = Fawe.instance().getQueueHandler().sync((Supplier) () -> new ServerLevel( server, server.executor, @@ -153,8 +153,7 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.dimension(), new LevelStem( originalServerWorld.dimensionTypeRegistration(), - originalServerWorld.getChunkSource().getGenerator() - ), + originalServerWorld.getChunkSource().getGenerator()), originalServerWorld.isDebug(), seed, ImmutableList.of(), @@ -162,13 +161,14 @@ protected boolean initNewWorld() throws Exception { originalServerWorld.getRandomSequences(), environment, generator, - biomeProvider - ) { + biomeProvider) { - private final Holder singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess() - .lookupOrThrow(BIOME).asHolderIdMap().byIdOrThrow( - WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) - ) : null; + private final Holder singleBiome = options.hasBiomeType() + ? DedicatedServer.getServer().registryAccess() + .lookupOrThrow(BIOME).asHolderIdMap().byIdOrThrow( + WorldEditPlugin.getInstance().getBukkitImplAdapter() + .getInternalBiomeId(options.getBiomeType())) + : null; @Override public @Nonnull Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { @@ -182,8 +182,7 @@ protected boolean initNewWorld() throws Exception { public void save( final ProgressListener progressListener, final boolean flush, - final boolean savingDisabled - ) { + final boolean savingDisabled) { // noop, spigot } @@ -192,20 +191,58 @@ public void save( final ProgressListener progressListener, final boolean flush, final boolean savingDisabled, - final boolean close - ) { + final boolean close) { // noop, paper } }).get(); freshWorld.noSave = true; removeWorldFromWorldsMap(); - newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name + newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); // rename to original world name if (paperConfigField != null) { paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); } + + if (FoliaLibHolder.isFolia()) { + return initWorldForFolia(newWorldData); + } + return true; } + private boolean initWorldForFolia(PrimaryLevelData worldData) throws ExecutionException, InterruptedException { + MinecraftServer console = ((CraftServer) Bukkit.getServer()).getServer(); + + ChunkPos spawnChunk = new ChunkPos( + freshWorld.getChunkSource().randomState().sampler().findSpawnPosition()); + + setRandomSpawnSelection(spawnChunk); + + CompletableFuture initFuture = new CompletableFuture<>(); + + FoliaLibHolder.getScheduler().runAtLocation( + freshWorld.getWorld().getChunkAt(spawnChunk.x, spawnChunk.z).getBlock(0, 0, 0).getLocation(), + task -> { + try { + console.initWorld(freshWorld, worldData, worldData.worldGenOptions()); + initFuture.complete(true); + } catch (Exception e) { + initFuture.completeExceptionally(e); + } + }); + + return initFuture.get(); + } + + private void setRandomSpawnSelection(ChunkPos spawnChunk) { + try { + Field randomSpawnField = ServerLevel.class.getDeclaredField("randomSpawnSelection"); + randomSpawnField.setAccessible(true); + randomSpawnField.set(freshWorld, spawnChunk); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to set randomSpawnSelection for Folia world initialization", e); + } + } + @Override protected void cleanup() { try { @@ -213,7 +250,7 @@ protected void cleanup() { } catch (Exception ignored) { } - //shutdown chunk provider + // shutdown chunk provider try { Fawe.instance().getQueueHandler().sync(() -> { try { @@ -226,29 +263,35 @@ protected void cleanup() { } catch (Exception ignored) { } - //remove world from server + // remove world from server try { Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap); } catch (Exception ignored) { } - //delete directory + // delete directory try { SafeFiles.tryHardToDeleteDir(tempDir); } catch (Exception ignored) { } } + @Override + protected World getFreshWorld() { + return freshWorld != null ? freshWorld.getWorld() : null; + } + @Override protected IChunkCache initSourceQueueCache() { return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } - //util + // util @SuppressWarnings("unchecked") private void removeWorldFromWorldsMap() { try { - Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + Map map = (Map) serverWorldsField + .get(Bukkit.getServer()); map.remove("faweregentempworld"); } catch (IllegalAccessException e) { throw new RuntimeException(e); diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index cbc0d2346a..d7082d367a 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -26,6 +26,10 @@ repositories { url = uri("https://maven.enginehub.org/repo/") } mavenCentral() + maven { + name = "TCodedReleases" + url = uri("https://repo.tcoded.com/releases") + } maven { name = "JitPack" url = uri("https://jitpack.io") @@ -93,6 +97,7 @@ dependencies { localImplementation(libs.log4j.api) implementation(libs.paperLib) + implementation(libs.foliaLib) compileOnly(libs.vault) { isTransitive = false } compileOnly(libs.dummypermscompat) { exclude("com.github.MilkBowl", "VaultAPI") @@ -215,6 +220,9 @@ tasks.withType().configureEach { relocate("io.papermc.lib", "com.sk89q.worldedit.bukkit.paperlib") { include(dependency("io.papermc:paperlib")) } + relocate("com.tcoded.folialib", "com.fastasyncworldedit.bukkit.folialib") { + include(dependency("com.tcoded:FoliaLib")) + } relocate("net.royawesome.jlibnoise", "com.sk89q.worldedit.jlibnoise") { include(dependency("com.sk89q.lib:jlibnoise")) } diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java index b902696d2f..d7f64bfefd 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java @@ -1,5 +1,6 @@ package com.fastasyncworldedit.bukkit.adapter; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.bukkit.BukkitAdapter; @@ -8,14 +9,19 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.TreeGenerator; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.TreeType; import org.bukkit.World; +import org.bukkit.block.Block; import org.bukkit.block.BlockState; -import java.util.Collection; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; /** * A base class for version-specific implementations of the BukkitImplAdapter @@ -23,7 +29,8 @@ * @param the version-specific NBT tag type * @param the version-specific ServerLevel type */ -public abstract class FaweAdapter extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter { +public abstract class FaweAdapter extends CachedBukkitAdapter + implements IDelegateBukkitImplAdapter { protected final BukkitImplAdapter parent; protected int[] ibdToOrdinal = null; @@ -40,15 +47,20 @@ public boolean generateTree( final TreeGenerator.TreeType treeType, final EditSession editSession, BlockVector3 blockVector3, - final World world - ) { + final World world) { TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType); if (bukkitType == TreeType.CHORUS_PLANT) { - // bukkit skips the feature gen which does this offset normally, so we have to add it back + // bukkit skips the feature gen which does this offset normally, so we have to + // add it back blockVector3 = blockVector3.add(BlockVector3.UNIT_Y); } BlockVector3 target = blockVector3; SERVER_LEVEL serverLevel = getServerLevel(world); + + if (FoliaLibHolder.isFolia()) { + return generateTreeFolia(bukkitType, editSession, target, world); + } + List placed = TaskManager.taskManager().sync(() -> { preCaptureStates(serverLevel); try { @@ -69,12 +81,91 @@ public boolean generateTree( continue; } editSession.setBlock(blockState.getX(), blockState.getY(), blockState.getZ(), - BukkitAdapter.adapt(blockState.getBlockData()) - ); + BukkitAdapter.adapt(blockState.getBlockData())); } return true; } + private boolean generateTreeFolia( + TreeType treeType, + EditSession editSession, + BlockVector3 target, + World world) { + if (Bukkit.isOwnedByCurrentRegion(world, target.x() >> 4, target.z() >> 4)) { + return generateTreeFoliaInternal(treeType, editSession, target, world); + } + + CompletableFuture future = new CompletableFuture<>(); + + org.bukkit.Location location = new org.bukkit.Location(world, target.x(), target.y(), target.z()); + FoliaLibHolder.getScheduler().runAtLocation( + location, + task -> { + try { + boolean result = generateTreeFoliaInternal(treeType, editSession, target, world); + future.complete(result); + } catch (Exception e) { + future.completeExceptionally(e); + } + }); + + try { + return future.get(); + } catch (Exception e) { + throw new RuntimeException("Failed to generate tree on Folia", e); + } + } + + private boolean generateTreeFoliaInternal( + TreeType treeType, + EditSession editSession, + BlockVector3 target, + World world) { + Set beforeBlocks = new HashSet<>(); + + int radius = 10; + int height = 32; + for (int x = -radius; x <= radius; x++) { + for (int y = -5; y <= height; y++) { + for (int z = -radius; z <= radius; z++) { + BlockVector3 pos = target.add(x, y, z); + org.bukkit.block.Block block = world.getBlockAt(pos.x(), pos.y(), pos.z()); + if (block.getType() != Material.AIR) { + beforeBlocks.add(pos); + } + } + } + } + + boolean generated = world.generateTree(BukkitAdapter.adapt(world, target), treeType); + + if (!generated) { + return false; + } + + List newBlocks = new ArrayList<>(); + for (int x = -radius; x <= radius; x++) { + for (int y = -5; y <= height; y++) { + for (int z = -radius; z <= radius; z++) { + BlockVector3 pos = target.add(x, y, z); + if (!beforeBlocks.contains(pos)) { + Block block = world.getBlockAt(pos.x(), pos.y(), pos.z()); + if (block.getType() != Material.AIR) { + newBlocks.add(block.getState()); + } + } + } + } + } + + for (BlockState blockState : newBlocks) { + editSession.setBlock(blockState.getX(), blockState.getY(), blockState.getZ(), + BukkitAdapter.adapt(blockState.getBlockData())); + } + + return !newBlocks.isEmpty(); + } + public void mapFromGlobalPalette(char[] data) { assert data.length == 4096; ensureInit(); diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/IBukkitAdapter.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/IBukkitAdapter.java index 27073d9458..7e734bdc1c 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/IBukkitAdapter.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/IBukkitAdapter.java @@ -1,6 +1,7 @@ package com.fastasyncworldedit.bukkit.adapter; import com.fastasyncworldedit.bukkit.util.BukkitItemStack; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.NotABlockException; @@ -49,8 +50,10 @@ public interface IBukkitAdapter { /** * Convert any WorldEdit world into an equivalent wrapped Bukkit world. * - *

If a matching world cannot be found, a {@link RuntimeException} - * will be thrown.

+ *

+ * If a matching world cannot be found, a {@link RuntimeException} + * will be thrown. + *

* * @param world the world * @return a wrapped Bukkit world @@ -99,8 +102,7 @@ default org.bukkit.Location adapt(org.bukkit.World world, Vector3 position) { checkNotNull(position); return new org.bukkit.Location( world, - position.x(), position.y(), position.z() - ); + position.x(), position.y(), position.z()); } default org.bukkit.Location adapt(org.bukkit.World world, BlockVector3 position) { @@ -121,8 +123,7 @@ default org.bukkit.Location adapt(org.bukkit.World world, Location location) { world, location.x(), location.y(), location.z(), location.getYaw(), - location.getPitch() - ); + location.getPitch()); } /** @@ -210,7 +211,6 @@ public synchronized Throwable fillInStackTrace() { return BlockTypes.get(material.getKey().toString()); } - /** * Converts a Material to a ItemType * @@ -369,30 +369,35 @@ default BlockState asBlockState(ItemStack itemStack) { * @param world World to "generate" tree from (seed-wise) * @return If successsful */ - default boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 pt, org.bukkit.World world) { + default boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 pt, + org.bukkit.World world) { TreeType bukkitType = BukkitWorld.toBukkitTreeType(type); if (bukkitType == TreeType.CHORUS_PLANT) { - pt = pt.add(0, 1, 0); // bukkit skips the feature gen which does this offset normally, so we have to add it back + pt = pt.add(0, 1, 0); // bukkit skips the feature gen which does this offset normally, so we have to + // add it back } return type != null && world.generateTree( BukkitAdapter.adapt(world, pt), bukkitType, - new EditSessionBlockChangeDelegate(editSession) - ); + new EditSessionBlockChangeDelegate(editSession)); } /** - * Retrieve the list of Bukkit entities ({@link org.bukkit.entity.Entity}) in the given world. If overridden by adapters + * Retrieve the list of Bukkit entities ({@link org.bukkit.entity.Entity}) in + * the given world. If overridden by adapters * will attempt retrieval asynchronously. * * @param world world to retrieve entities in * @return list of {@link org.bukkit.entity.Entity} */ default List getEntities(org.bukkit.World world) { - return TaskManager.taskManager().sync(world::getEntities); + return FoliaLibHolder.isFolia() + ? TaskManager.taskManager().syncWhenFree(world::getEntities) + : TaskManager.taskManager().sync(world::getEntities); } /** - * Import Minecraft internal features into FAWE. Should be executed after worlds loading (in order to capture datapacks) + * Import Minecraft internal features into FAWE. Should be executed after worlds + * loading (in order to capture datapacks) * * @since 2.14.1 */ diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java index 0fce7c7dfd..c466a63633 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java @@ -1,5 +1,7 @@ package com.fastasyncworldedit.bukkit.adapter; +import com.tcoded.folialib.wrapper.task.WrappedTask; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; @@ -8,6 +10,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitWorld; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; @@ -15,6 +18,9 @@ import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.WorldInfo; import org.jetbrains.annotations.NotNull; @@ -36,17 +42,20 @@ public abstract class Regenerator { protected final Extent target; protected final RegenOptions options; - //runtime + // runtime protected long seed; protected SingleThreadQueueExtent source; /** * Initializes an abstract regeneration handler. * - * @param originalBukkitWorld the Bukkit world containing all the information on how to regenerate the {code Region} + * @param originalBukkitWorld the Bukkit world containing all the information on + * how to regenerate the {code Region} * @param region the selection to regenerate - * @param target the target {@code Extent} to paste the regenerated blocks into - * @param options the options to used while regenerating and pasting into the target {@code Extent} + * @param target the target {@code Extent} to paste the regenerated + * blocks into + * @param options the options to used while regenerating and pasting + * into the target {@code Extent} */ public Regenerator(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) { this.originalBukkitWorld = originalBukkitWorld; @@ -96,19 +105,35 @@ private void createSource() { source = new SingleThreadQueueExtent( BukkitWorld.HAS_MIN_Y ? originalBukkitWorld.getMinHeight() : 0, - BukkitWorld.HAS_MIN_Y ? originalBukkitWorld.getMaxHeight() : 256 - ); + BukkitWorld.HAS_MIN_Y ? originalBukkitWorld.getMaxHeight() : 256); source.init(target, initSourceQueueCache(), null); } 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 + WrappedTask foliaTask = null; + int taskId = -1; + if (FoliaLibHolder.isFolia()) { + World freshWorld = getFreshWorld(); + World world = freshWorld != null ? freshWorld : originalBukkitWorld; + BlockVector3 min = region.getMinimumPoint(); + Location location = new Location(world, min.x(), min.y(), min.z()); + foliaTask = FoliaLibHolder.getScheduler().runAtLocationTimer( + location, + () -> { + final long startTime = System.nanoTime(); + runTasks(() -> System.nanoTime() - startTime < timeoutPerTick); + }, + 1, + 1); + } else { + 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(); BiomeType biome = options.getBiomeType(); @@ -126,7 +151,22 @@ private void copyToWorld() { }); } target.setBlocks(region, pattern); - TaskManager.taskManager().cancel(taskId); + if (foliaTask != null) { + FoliaLibHolder.getScheduler().cancelTask(foliaTask); + } else if (taskId != -1) { + TaskManager.taskManager().cancel(taskId); + } + } + + /** + * Get the fresh world for Folia region scheduling. + * Subclasses should override this method to return the fresh world if + * available. + * + * @return the fresh world, or null if not available + */ + protected @Nullable World getFreshWorld() { + return null; } private abstract class ChunkwisePattern implements Pattern { @@ -158,7 +198,8 @@ public BaseBlock applyBlock(final BlockVector3 position) { } @Override - public boolean apply(final Extent extent, final BlockVector3 get, final BlockVector3 set) throws WorldEditException { + public boolean apply(final Extent extent, final BlockVector3 get, final BlockVector3 set) + throws WorldEditException { BaseBlock fullBlock; if (chunk != null) { fullBlock = chunk.getFullBlock(get.x() & 15, get.y(), get.z() & 15); @@ -188,7 +229,8 @@ public BaseBlock applyBlock(final BlockVector3 position) { } @Override - public boolean apply(final Extent extent, final BlockVector3 get, final BlockVector3 set) throws WorldEditException { + public boolean apply(final Extent extent, final BlockVector3 get, final BlockVector3 set) + throws WorldEditException { final BaseBlock fullBlock; if (chunk != null) { fullBlock = chunk.getFullBlock(get.x() & 15, get.y(), get.z() & 15); @@ -205,17 +247,25 @@ public Pattern fork() { } } - //functions to be implemented by sub class + // functions to be implemented by sub class private void cleanup0() { cleanup(); } /** - *

Implement the preparation process in here. DO NOT instanciate any variable here that require the cleanup function. This function is for gathering further information before initializing a new - * world.

+ *

+ * Implement the preparation process in here. DO NOT instanciate any variable + * here that require the cleanup function. This function is for gathering + * further information before initializing a new + * world. + *

* - *

Fields required to be initialized: chunkStati, seed

- *

For chunkStati also see {code ChunkStatusWrapper}.

+ *

+ * Fields required to be initialized: chunkStati, seed + *

+ *

+ * For chunkStati also see {code ChunkStatusWrapper}. + *

* * @return whether or not the preparation process was successful */ @@ -226,20 +276,27 @@ private void cleanup0() { *

* Fields required to be initialized: generateConcurrent * - * @return true if everything went fine, otherwise false. When false is returned the Regenerator halts the regeneration process and calls the cleanup function. - * @throws java.lang.Exception When the implementation of this method throws and exception the Regenerator halts the regeneration process and calls the cleanup function. + * @return true if everything went fine, otherwise false. When false is returned + * the Regenerator halts the regeneration process and calls the cleanup + * function. + * @throws java.lang.Exception When the implementation of this method throws and + * exception the Regenerator halts the regeneration + * process and calls the cleanup function. */ protected abstract boolean initNewWorld() throws Exception; - //functions to implement by sub class - regenate related + // functions to implement by sub class - regenate related /** - * Implement the cleanup of all the mess that is created during the regeneration process (initNewWorld() and generate()).This function must not throw any exceptions. + * Implement the cleanup of all the mess that is created during the regeneration + * process (initNewWorld() and generate()).This function must not throw any + * exceptions. */ protected abstract void cleanup(); /** - * Implement the initialization an {@code IChunkCache} here. Use will need the {@code getChunkAt} function + * Implement the initialization an {@code IChunkCache} here. Use will + * need the {@code getChunkAt} function * * @return an initialized {@code IChunkCache} */ @@ -252,7 +309,7 @@ protected BiomeProvider getBiomeProvider() { return originalBukkitWorld.getBiomeProvider(); } - //classes + // classes public enum Concurrency { FULL, diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/BukkitTaskManager.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/BukkitTaskManager.java index 11b8565a67..10bf770cb0 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/BukkitTaskManager.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/BukkitTaskManager.java @@ -1,11 +1,16 @@ package com.fastasyncworldedit.bukkit.util; import com.fastasyncworldedit.core.util.TaskManager; +import com.tcoded.folialib.impl.PlatformScheduler; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; import javax.annotation.Nonnull; +/** + * Task manager implementation for Bukkit/Paper/Folia servers. + * Uses FoliaLib for cross-platform scheduler compatibility. + */ public class BukkitTaskManager extends TaskManager { private final Plugin plugin; @@ -14,40 +19,64 @@ public BukkitTaskManager(final Plugin plugin) { this.plugin = plugin; } + private PlatformScheduler getScheduler() { + return FoliaLibHolder.getScheduler(); + } + @Override public int repeat(@Nonnull final Runnable runnable, final int interval) { - return this.plugin.getServer().getScheduler().scheduleSyncRepeatingTask(this.plugin, runnable, interval, interval); + // FoliaLib handles the difference between Folia and non-Folia servers + getScheduler().runTimer(runnable, interval, interval); + // FoliaLib doesn't return task IDs in the same way, return 0 as a placeholder + return 0; } @Override public int repeatAsync(@Nonnull final Runnable runnable, final int interval) { - return this.plugin.getServer().getScheduler().scheduleAsyncRepeatingTask(this.plugin, runnable, interval, interval); + getScheduler().runTimerAsync(runnable, interval, interval); + return 0; } @Override public void async(@Nonnull final Runnable runnable) { - this.plugin.getServer().getScheduler().runTaskAsynchronously(this.plugin, runnable).getTaskId(); + getScheduler().runAsync(task -> runnable.run()); } @Override public void task(@Nonnull final Runnable runnable) { - this.plugin.getServer().getScheduler().runTask(this.plugin, runnable).getTaskId(); + getScheduler().runNextTick(task -> runnable.run()); } @Override public void later(@Nonnull final Runnable runnable, final int delay) { - this.plugin.getServer().getScheduler().runTaskLater(this.plugin, runnable, delay).getTaskId(); + if (delay <= 0) { + getScheduler().runNextTick(task -> runnable.run()); + } else { + getScheduler().runLater(runnable, delay); + } } @Override public void laterAsync(@Nonnull final Runnable runnable, final int delay) { - this.plugin.getServer().getScheduler().runTaskLaterAsynchronously(this.plugin, runnable, delay); + if (delay <= 0) { + getScheduler().runAsync(task -> runnable.run()); + } else { + getScheduler().runLaterAsync(runnable, delay); + } } @Override public void cancel(final int task) { - if (task != -1) { - Bukkit.getScheduler().cancelTask(task); + // FoliaLib manages tasks internally, and we don't have individual task IDs + // This method is mostly a no-op with FoliaLib since it uses WrappedTask + // For legacy compatibility, try to cancel via Bukkit scheduler on non-Folia + // servers + if (task != -1 && !FoliaLibHolder.isFolia()) { + try { + Bukkit.getScheduler().cancelTask(task); + } catch (Exception ignored) { + // May not be a valid task ID if it was scheduled via FoliaLib + } } } diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaLibHolder.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaLibHolder.java new file mode 100644 index 0000000000..91945096de --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaLibHolder.java @@ -0,0 +1,113 @@ +package com.fastasyncworldedit.bukkit.util; + +import com.fastasyncworldedit.core.Fawe; +import com.tcoded.folialib.FoliaLib; +import com.tcoded.folialib.impl.PlatformScheduler; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Holds the FoliaLib instance for the plugin. + * This class provides centralized access to FoliaLib functionality. + */ +public final class FoliaLibHolder { + + private static FoliaLib foliaLib; + + private FoliaLibHolder() { + // Utility class + } + + /** + * Initialize the FoliaLib instance with the plugin. + * This should be called once during plugin startup. + * + * @param plugin The plugin instance + */ + public static void init(@NotNull Plugin plugin) { + if (foliaLib == null) { + foliaLib = new FoliaLib(plugin); + // Set the Folia flag in Fawe for core module to use + Fawe.setFoliaServer(foliaLib.isFolia()); + } + } + + /** + * Get the FoliaLib instance. + * + * @return The FoliaLib instance, or null if not initialized + */ + @Nullable + public static FoliaLib getFoliaLib() { + return foliaLib; + } + + /** + * Get the scheduler from FoliaLib. + * + * @return The platform scheduler + * @throws IllegalStateException if FoliaLib is not initialized + */ + @NotNull + public static PlatformScheduler getScheduler() { + if (foliaLib == null) { + throw new IllegalStateException("FoliaLib has not been initialized. Call init() first."); + } + return foliaLib.getScheduler(); + } + + /** + * Check if the server is running Folia. + * + * @return true if running on Folia, false otherwise + */ + public static boolean isFolia() { + if (foliaLib == null) { + // Fall back to class detection if not initialized + try { + Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + return foliaLib.isFolia(); + } + + /** + * Check if the server is running Paper. + * + * @return true if running on Paper, false otherwise + */ + public static boolean isPaper() { + if (foliaLib == null) { + return false; + } + return foliaLib.isPaper(); + } + + /** + * Check if the server is running Spigot. + * + * @return true if running on Spigot, false otherwise + */ + public static boolean isSpigot() { + if (foliaLib == null) { + return false; + } + return foliaLib.isSpigot(); + } + + /** + * Shutdown and cleanup FoliaLib resources. + * Should be called during plugin disable. + */ + public static void shutdown() { + if (foliaLib != null) { + foliaLib.getScheduler().cancelAllTasks(); + foliaLib = null; + } + } + +} \ No newline at end of file diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCommandSender.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCommandSender.java index 3c2e99f420..add2aba87d 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCommandSender.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCommandSender.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.bukkit; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.platform.AbstractCommandBlockActor; @@ -65,63 +66,63 @@ public String getName() { @Override @Deprecated public void printRaw(String msg) { - //FAWE start - ensure executed on main thread + // FAWE start - ensure executed on main thread TaskManager.taskManager().sync(() -> { for (String part : msg.split("\n")) { sender.sendMessage(part); } return null; }); - //FAWE end + // FAWE end } @Override @Deprecated public void print(String msg) { - //FAWE start - ensure executed on main thread + // FAWE start - ensure executed on main thread TaskManager.taskManager().sync(() -> { for (String part : msg.split("\n")) { print(TextComponent.of(part, TextColor.LIGHT_PURPLE)); } return null; }); - //FAWE end + // FAWE end } @Override @Deprecated public void printDebug(String msg) { - //FAWE start - ensure executed on main thread + // FAWE start - ensure executed on main thread TaskManager.taskManager().sync(() -> { for (String part : msg.split("\n")) { print(TextComponent.of(part, TextColor.GRAY)); } return null; }); - //FAWE end + // FAWE end } @Override @Deprecated public void printError(String msg) { - //FAWE start - ensure executed on main thread + // FAWE start - ensure executed on main thread TaskManager.taskManager().sync(() -> { for (String part : msg.split("\n")) { print(TextComponent.of(part, TextColor.RED)); } return null; }); - //FAWE end + // FAWE end } @Override public void print(Component component) { - //FAWE start - ensure executed on main thread + // FAWE start - ensure executed on main thread TaskManager.taskManager().sync(() -> { TextAdapter.sendMessage(sender, WorldEditText.format(component, getLocale())); return null; }); - //FAWE end + // FAWE end } @Override @@ -151,12 +152,12 @@ public boolean hasPermission(String permission) { return sender.hasPermission(permission); } - //FAWE start + // FAWE start @Override public boolean togglePermission(String permission) { return true; } - //FAWE end + // FAWE end @Override public void setPermission(String permission, boolean value) { @@ -195,14 +196,16 @@ public boolean isActive() { // we can update eagerly updateActive(); } else { + if (FoliaLibHolder.isFolia()) { + return active; + } // we should update it eventually Bukkit.getScheduler().callSyncMethod( plugin, () -> { updateActive(); return null; - } - ); + }); } return active; } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitEntity.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitEntity.java index adf0ee8465..b98b7edd71 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitEntity.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitEntity.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.bukkit; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.entity.BaseEntity; @@ -28,24 +29,26 @@ import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.NullWorld; +import io.papermc.lib.PaperLib; import org.bukkit.entity.EntityType; import javax.annotation.Nullable; import java.lang.ref.WeakReference; +import java.util.concurrent.CompletableFuture; import static com.google.common.base.Preconditions.checkNotNull; /** * An adapter to adapt a Bukkit entity into a WorldEdit one. */ -//FAWE start - made class public +// FAWE start - made class public public class BukkitEntity implements Entity { -//FAWE end + // FAWE end private final WeakReference entityRef; - //FAWE start + // FAWE start private final EntityType type; - //FAWE end + // FAWE end /** * Create a new instance. @@ -54,9 +57,9 @@ public class BukkitEntity implements Entity { */ public BukkitEntity(org.bukkit.entity.Entity entity) { checkNotNull(entity); - //FAWE start + // FAWE start this.type = entity.getType(); - //FAWE end + // FAWE end this.entityRef = new WeakReference<>(entity); } @@ -84,6 +87,9 @@ public Location getLocation() { public boolean setLocation(Location location) { org.bukkit.entity.Entity entity = entityRef.get(); if (entity != null) { + if (PaperLib.isPaper()) { + return entity.teleportAsync(BukkitAdapter.adapt(location)).join(); + } return entity.teleport(BukkitAdapter.adapt(location)); } else { return false; @@ -100,6 +106,14 @@ public BaseEntity getState() { BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); if (adapter != null) { + if (FoliaLibHolder.isFolia()) { + org.bukkit.Location location = entity.getLocation(); + CompletableFuture future = new CompletableFuture<>(); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> future.complete(adapter.getEntity(entity))); + return future.join(); + } return adapter.getEntity(entity); } else { return null; @@ -111,8 +125,25 @@ public BaseEntity getState() { @Override public boolean remove() { - // synchronize the whole method, not just the remove operation as we always need to synchronize and - // can make sure the entity reference was not invalidated in the few milliseconds between the next available tick (lol) + if (FoliaLibHolder.isFolia()) { + return TaskManager.taskManager().syncWhenFree(() -> { + org.bukkit.entity.Entity entity = entityRef.get(); + if (entity != null) { + try { + FoliaLibHolder.getScheduler().runAtEntity(entity, scheduledTask -> entity.remove()); + return true; + } catch (UnsupportedOperationException e) { + return false; + } + } else { + return true; + } + }); + } + // synchronize the whole method, not just the remove operation as we always need + // to synchronize and + // can make sure the entity reference was not invalidated in the few + // milliseconds between the next available tick (lol) return TaskManager.taskManager().sync(() -> { org.bukkit.entity.Entity entity = entityRef.get(); if (entity != null) { diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitEntityProperties.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitEntityProperties.java index 28ef9274a1..7b6855319d 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitEntityProperties.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitEntityProperties.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.bukkit; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.sk89q.worldedit.entity.metadata.EntityProperties; import org.bukkit.entity.AbstractVillager; import org.bukkit.entity.Ambient; @@ -148,11 +149,17 @@ public boolean isGolem() { @Override public boolean isTamed() { + if (FoliaLibHolder.isFolia()) { + return false; + } return entity instanceof Tameable && ((Tameable) entity).isTamed(); } @Override public boolean isTagged() { + if (FoliaLibHolder.isFolia()) { + return false; + } return entity instanceof LivingEntity && entity.getCustomName() != null; } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java index a7a680b68b..cb8d95ee5a 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java @@ -22,6 +22,7 @@ import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.util.TaskManager; +import io.papermc.lib.PaperLib; import com.sk89q.util.StringUtil; import com.sk89q.wepif.VaultResolver; import com.sk89q.worldedit.WorldEdit; @@ -242,6 +243,16 @@ public boolean trySetPosition(Vector3 pos, float pitch, float yaw) { } org.bukkit.World finalWorld = world; //FAWE end + if (PaperLib.isPaper()) { + return player.teleportAsync(new Location( + finalWorld, + pos.x(), + pos.y(), + pos.z(), + yaw, + pitch + )).join(); + } return TaskManager.taskManager().sync(() -> player.teleport(new Location( finalWorld, pos.x(), @@ -363,6 +374,9 @@ public com.sk89q.worldedit.util.Location getLocation() { @Override public boolean setLocation(com.sk89q.worldedit.util.Location location) { + if (PaperLib.isPaper()) { + return player.teleportAsync(BukkitAdapter.adapt(location)).join(); + } return player.teleport(BukkitAdapter.adapt(location)); } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index 73a7421c34..120428ee5c 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.bukkit; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.bukkit.util.MinecraftVersion; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor; @@ -75,9 +76,9 @@ public class BukkitServerInterface extends AbstractPlatform implements MultiUser public final WorldEditPlugin plugin; private final CommandRegistration dynamicCommands; private final Lifecycled watchdog; - //FAWE start + // FAWE start private RelighterFactory relighterFactory; - //FAWE end + // FAWE end private boolean hookingEvents; public BukkitServerInterface(WorldEditPlugin plugin, Server server) { @@ -124,7 +125,8 @@ public boolean isValidMobType(String type) { if (!type.startsWith("minecraft:")) { return false; } - @SuppressWarnings("deprecation") final EntityType entityType = EntityType.fromName(type.substring(10)); + @SuppressWarnings("deprecation") + final EntityType entityType = EntityType.fromName(type.substring(10)); return entityType != null && entityType.isAlive(); } @@ -135,6 +137,9 @@ public void reload() { @Override public int schedule(long delay, long period, Runnable task) { + if (FoliaLibHolder.isFolia()) { + return 0; + } return Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, task, delay, period); } @@ -191,19 +196,17 @@ public void registerCommands(CommandManager dispatcher) { String[] aliases = Stream.concat( Stream.of(command.getName()), - command.getAliases().stream() - ).toArray(String[]::new); + command.getAliases().stream()).toArray(String[]::new); // TODO Handle localisation correctly return new CommandInfo( reduceToText( command.getUsage(), - WorldEdit.getInstance().getConfiguration().defaultLocale - ), - reduceToText(command.getDescription(), WorldEdit.getInstance().getConfiguration().defaultLocale), + WorldEdit.getInstance().getConfiguration().defaultLocale), + reduceToText(command.getDescription(), + WorldEdit.getInstance().getConfiguration().defaultLocale), aliases, inspector, - permissionsArray - ); + permissionsArray); }).collect(Collectors.toList())); } @@ -232,12 +235,12 @@ public String getPlatformVersion() { return plugin.getDescription().getVersion(); } - //FAWE start + // FAWE start @Override public String id() { return "intellectualsites:bukkit"; } - //FAWE end + // FAWE end @Override public Map getCapabilities() { @@ -252,8 +255,7 @@ public Map getCapabilities() { } private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( - SideEffect.NEIGHBORS - ); + SideEffect.NEIGHBORS); @Override public Set getSupportedSideEffects() { @@ -284,10 +286,9 @@ public Collection getConnectedUsers() { return users; } - //FAWE start + // FAWE start @Override - public @Nonnull - RelighterFactory getRelighterFactory() { + public @Nonnull RelighterFactory getRelighterFactory() { if (this.relighterFactory == null) { this.relighterFactory = this.plugin.getBukkitImplAdapter().getRelighterFactory(); LOGGER.info("Using {} as relighter factory.", this.relighterFactory.getClass().getCanonicalName()); @@ -318,5 +319,5 @@ public IBatchProcessor getPlatformPostProcessor(boolean fastMode) { public PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, Region region) { return this.plugin.getBukkitImplAdapter().getPlatformPlacementProcessor(extent, mask, region); } - //FAWE end + // FAWE end } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index 99aaefdd12..4dbe76a758 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -27,6 +27,7 @@ import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; @@ -66,12 +67,14 @@ import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockState; +import org.bukkit.Location; import org.bukkit.block.Chest; import org.bukkit.entity.Entity; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import java.lang.ref.WeakReference; +import java.util.concurrent.CompletableFuture; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; @@ -90,9 +93,9 @@ public class BukkitWorld extends AbstractWorld { private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final boolean HAS_3D_BIOMES; - //FAWE start - allow access for easy checking if World#getMin/MaxHeight exists + // FAWE start - allow access for easy checking if World#getMin/MaxHeight exists public static final boolean HAS_MIN_Y; - //FAWE end + // FAWE end private static final Map effects = new HashMap<>(); @@ -121,9 +124,9 @@ public class BukkitWorld extends AbstractWorld { } protected WeakReference worldRef; - //FAWE start + // FAWE start protected final String worldNameRef; - //FAWE end + // FAWE end private final WorldNativeAccess worldNativeAccess; /** @@ -133,9 +136,9 @@ public class BukkitWorld extends AbstractWorld { */ public BukkitWorld(World world) { this.worldRef = new WeakReference<>(world); - //FAWE start + // FAWE start this.worldNameRef = world.getName(); - //FAWE end + // FAWE end BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); if (adapter != null) { this.worldNativeAccess = adapter.createWorldNativeAccess(world); @@ -144,11 +147,24 @@ public BukkitWorld(World world) { } } + private T syncRegion(BlockVector3 position, java.util.function.Supplier supplier) { + if (FoliaLibHolder.isFolia()) { + World world = getWorld(); + Location location = new Location(world, position.x(), position.y(), position.z()); + CompletableFuture future = new CompletableFuture<>(); + FoliaLibHolder.getScheduler().runAtLocation( + location, + scheduledTask -> future.complete(supplier.get())); + return future.join(); + } + return TaskManager.taskManager().sync(supplier); + } + @Override public List getEntities(Region region) { World world = getWorld(); - List ents = TaskManager.taskManager().sync(world::getEntities); + List ents = syncRegion(region.getMinimumPoint(), world::getEntities); List entities = new ArrayList<>(); for (Entity ent : ents) { if (region.contains(BukkitAdapter.asBlockVector(ent.getLocation()))) { @@ -162,7 +178,7 @@ public List getEntities(Region region) { public List getEntities() { List list = new ArrayList<>(); - List ents = TaskManager.taskManager().sync(getWorld()::getEntities); + List ents = syncRegion(BlockVector3.ZERO, getWorld()::getEntities); for (Entity entity : ents) { list.add(BukkitAdapter.adapt(entity)); } @@ -172,12 +188,12 @@ public List getEntities() { @Override public int removeEntities(final Region region) { List entities = getEntities(region); - return TaskManager.taskManager().sync(() -> entities.stream() - .mapToInt(entity -> entity.remove() ? 1 : 0).sum() - ); + return syncRegion(region.getMinimumPoint(), () -> entities.stream() + .mapToInt(entity -> entity.remove() ? 1 : 0).sum()); } - //FAWE: createEntity was moved to IChunkExtent to prevent issues with Async Entity Add. + // FAWE: createEntity was moved to IChunkExtent to prevent issues with Async + // Entity Add. /** * Get the world handle. @@ -185,7 +201,7 @@ public int removeEntities(final Region region) { * @return the world */ public World getWorld() { - //FAWE start + // FAWE start World tmp = worldRef.get(); if (tmp == null) { tmp = Bukkit.getWorld(worldNameRef); @@ -193,11 +209,11 @@ public World getWorld() { worldRef = new WeakReference<>(tmp); } } - //FAWE end + // FAWE end return checkNotNull(tmp, "The world was unloaded and the reference is unavailable"); } - //FAWE start + // FAWE start /** * Get the world handle. @@ -217,21 +233,22 @@ protected World getWorldChecked() throws WorldEditException { } return tmp; } - //FAWE end + // FAWE end @Override public String getName() { - //FAWE start - Throw WorldUnloadedException rather than NPE when world unloaded and attempted to be accessed + // FAWE start - Throw WorldUnloadedException rather than NPE when world unloaded + // and attempted to be accessed return getWorldChecked().getName(); - //FAWE end + // FAWE end } - //FAWE start - allow history to read an unloaded world's name + // FAWE start - allow history to read an unloaded world's name @Override public String getNameUnsafe() { return worldNameRef; } - //FAWE end + // FAWE end @Override public String id() { @@ -254,9 +271,9 @@ public Path getStoragePath() { @Override public int getBlockLightLevel(BlockVector3 pt) { - //FAWE start - safe edit region + // FAWE start - safe edit region testCoords(pt); - //FAWE end + // FAWE end return getWorld().getBlockAt(pt.x(), pt.y(), pt.z()).getLightLevel(); } @@ -280,9 +297,9 @@ public boolean regenerate(Region region, Extent extent, RegenOptions options) { @Override public boolean clearContainerBlockContents(BlockVector3 pt) { checkNotNull(pt); - //FAWE start - safe edit region + // FAWE start - safe edit region testCoords(pt); - //FAWE end + // FAWE end BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); if (adapter != null) { try { @@ -313,10 +330,11 @@ public boolean clearContainerBlockContents(BlockVector3 pt) { } /** - * An EnumMap that stores which WorldEdit TreeTypes apply to which Bukkit TreeTypes. + * An EnumMap that stores which WorldEdit TreeTypes apply to which Bukkit + * TreeTypes. */ - private static final EnumMap treeTypeMapping = - new EnumMap<>(TreeGenerator.TreeType.class); + private static final EnumMap treeTypeMapping = new EnumMap<>( + TreeGenerator.TreeType.class); static { for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) { @@ -337,13 +355,16 @@ public boolean clearContainerBlockContents(BlockVector3 pt) { treeTypeMapping.put(TreeGenerator.TreeType.RANDOM_MUSHROOM, TreeType.BROWN_MUSHROOM); for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) { if (treeTypeMapping.get(type) == null) { - //FAWE start + // FAWE start LOGGER.info("No TreeType mapping for TreeGenerator.TreeType." + type); LOGGER.info("The above message is displayed because your FAWE version is newer than {}" + - " and contains features of future minecraft versions which do not exist in {} hence the tree type" + - " {} is not available. This is not an error. This version of FAWE will work on your version of " + - " Minecraft. This is an informative message only.", Bukkit.getVersion(), Bukkit.getVersion(), type); - //FAWE end + " and contains features of future minecraft versions which do not exist in {} hence the tree type" + + + " {} is not available. This is not an error. This version of FAWE will work on your version of " + + + " Minecraft. This is an informative message only.", Bukkit.getVersion(), Bukkit.getVersion(), + type); + // FAWE end } } } @@ -354,10 +375,10 @@ public static TreeType toBukkitTreeType(TreeGenerator.TreeType type) { @Override public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 pt) { - //FAWE start - allow tree commands to be undone and obey region restrictions + // FAWE start - allow tree commands to be undone and obey region restrictions testCoords(pt); return WorldEditPlugin.getInstance().getBukkitImplAdapter().generateTree(type, editSession, pt, getWorld()); - //FAWE end + // FAWE end } @Override @@ -368,11 +389,11 @@ public void dropItem(Vector3 pt, BaseItemStack item) { @Override public void checkLoadedChunk(BlockVector3 pt) { - //FAWE start - safe edit region + // FAWE start - safe edit region testCoords(pt); - //FAWE end + // FAWE end World world = getWorld(); - //FAWE start + // FAWE start int X = pt.x() >> 4; int Z = pt.z() >> 4; if (Fawe.isMainThread()) { @@ -380,7 +401,7 @@ public void checkLoadedChunk(BlockVector3 pt) { } else if (PaperLib.isPaper()) { PaperLib.getChunkAtAsync(world, X, Z, true); } - //FAWE end + // FAWE end } @Override @@ -441,14 +462,14 @@ public boolean playEffect(Vector3 position, int type, int data) { return true; } - //FAWE start - allow block break effect of non-legacy blocks + // FAWE start - allow block break effect of non-legacy blocks @Override public boolean playBlockBreakEffect(Vector3 position, BlockType type) { World world = getWorld(); world.playEffect(BukkitAdapter.adapt(world, position), Effect.STEP_SOUND, BukkitAdapter.adapt(type)); return true; } - //FAWE end + // FAWE end @Override public WeatherType getWeather() { @@ -502,25 +523,25 @@ public BlockVector3 getSpawnPosition() { @Override public void simulateBlockMine(BlockVector3 pt) { - //FAWE start - safe edit region + // FAWE start - safe edit region testCoords(pt); - //FAWE end + // FAWE end getWorld().getBlockAt(pt.x(), pt.y(), pt.z()).breakNaturally(); } - //FAWE start + // FAWE start @Override public Collection getBlockDrops(BlockVector3 position) { return getWorld().getBlockAt(position.x(), position.y(), position.z()).getDrops().stream() .map(BukkitAdapter::adapt).collect(Collectors.toList()); } - //FAWE end + // FAWE end @Override public boolean canPlaceAt(BlockVector3 position, com.sk89q.worldedit.world.block.BlockState blockState) { - //FAWE start - safe edit region + // FAWE start - safe edit region testCoords(position); - //FAWE end + // FAWE end BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); if (adapter != null) { return adapter.canPlaceAt(getWorld(), position, blockState); @@ -553,9 +574,9 @@ public boolean generateStructure(StructureType type, EditSession editSession, Bl @Override public com.sk89q.worldedit.world.block.BlockState getBlock(BlockVector3 position) { - //FAWE start - safe edit region + // FAWE start - safe edit region testCoords(position); - //FAWE end + // FAWE end BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); if (adapter != null) { try { @@ -577,9 +598,9 @@ public com.sk89q.worldedit.world.block.BlockState getBlock(BlockVector3 position @Override public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) { - //FAWE start - safe edit region + // FAWE start - safe edit region testCoords(position); - //FAWE end + // FAWE end if (worldNativeAccess != null) { try { return worldNativeAccess.setBlock(position, block, sideEffects); @@ -599,9 +620,9 @@ public > boolean setBlock(BlockVector3 position, B @Override public BaseBlock getFullBlock(BlockVector3 position) { - //FAWE start - safe edit region + // FAWE start - safe edit region testCoords(position); - //FAWE end + // FAWE end BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); if (adapter != null) { return adapter.getFullBlock(BukkitAdapter.adapt(getWorld(), position)); @@ -624,17 +645,15 @@ private void testCoords(BlockVector3 position) throws FaweException { @Override public Set applySideEffects( BlockVector3 position, com.sk89q.worldedit.world.block.BlockState previousType, - SideEffectSet sideEffectSet - ) { - //FAWE start - safe edit region + SideEffectSet sideEffectSet) { + // FAWE start - safe edit region testCoords(position); - //FAWE end + // FAWE end if (worldNativeAccess != null) { worldNativeAccess.applySideEffects(position, previousType, sideEffectSet); return Sets.intersection( WorldEditPlugin.getInstance().getInternalPlatform().getSupportedSideEffects(), - sideEffectSet.getSideEffectsToApply() - ); + sideEffectSet.getSideEffectsToApply()); } return ImmutableSet.of(); @@ -642,9 +661,9 @@ public Set applySideEffects( @Override public boolean useItem(BlockVector3 position, BaseItem item, Direction face) { - //FAWE start - safe edit region + // FAWE start - safe edit region testCoords(position); - //FAWE end + // FAWE end BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); if (adapter != null) { return adapter.simulateItemUse(getWorld(), position, item, face); @@ -662,9 +681,9 @@ public boolean fullySupports3DBiomes() { @SuppressWarnings("deprecation") @Override public BiomeType getBiome(BlockVector3 position) { - //FAWE start - safe edit region + // FAWE start - safe edit region testCoords(position); - //FAWE end + // FAWE end if (HAS_3D_BIOMES) { return BukkitAdapter.adapt(getWorld().getBiome(position.x(), position.y(), position.z())); } else { @@ -675,9 +694,9 @@ public BiomeType getBiome(BlockVector3 position) { @SuppressWarnings("deprecation") @Override public boolean setBiome(BlockVector3 position, BiomeType biome) { - //FAWE start - safe edit region + // FAWE start - safe edit region testCoords(position); - //FAWE end + // FAWE end if (HAS_3D_BIOMES) { getWorld().setBiome(position.x(), position.y(), position.z(), BukkitAdapter.adapt(biome)); } else { @@ -686,7 +705,7 @@ public boolean setBiome(BlockVector3 position, BiomeType biome) { return true; } - //FAWE start + // FAWE start @Override public > boolean setBlock(int x, int y, int z, T block) @@ -728,5 +747,5 @@ public void flush() { worldNativeAccess.flush(); } } - //FAWE end + // FAWE end } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index d0708887b0..99ab33062b 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -21,6 +21,7 @@ import com.fastasyncworldedit.bukkit.BukkitPermissionAttachmentManager; import com.fastasyncworldedit.bukkit.FaweBukkit; +import com.fastasyncworldedit.bukkit.util.FoliaLibHolder; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.util.UpdateNotification; import com.fastasyncworldedit.core.util.WEManager; @@ -102,17 +103,17 @@ /** * Plugin for Bukkit. */ -//FAWE start - Don't implement TabCompleter, we use Paper's AsyncTabCompleteListener +// FAWE start - Don't implement TabCompleter, we use Paper's +// AsyncTabCompleteListener public class WorldEditPlugin extends JavaPlugin { -//FAWE end + // FAWE end private static final Logger LOGGER = LogManagerCompat.getLogger(); public static final String CUI_PLUGIN_CHANNEL = "worldedit:cui"; private static WorldEditPlugin INSTANCE; private static final int BSTATS_ID = 1403; - private final SimpleLifecycled adapter = - SimpleLifecycled.invalid(); + private final SimpleLifecycled adapter = SimpleLifecycled.invalid(); private BukkitServerInterface platform; private BukkitConfiguration config; private BukkitPermissionAttachmentManager permissionAttachmentManager; @@ -123,79 +124,83 @@ public class WorldEditPlugin extends JavaPlugin { @Override public void onLoad() { - //FAWE start - // This is already covered by Spigot, however, a more pesky warning with a proper explanation over "Ambiguous plugin name..." can't hurt. + // FAWE start + // This is already covered by Spigot, however, a more pesky warning with a + // proper explanation over "Ambiguous plugin name..." can't hurt. Plugin[] plugins = Bukkit.getServer().getPluginManager().getPlugins(); for (Plugin p : plugins) { if (p.getName().equals("WorldEdit")) { LOGGER.warn( - "You installed WorldEdit alongside FastAsyncWorldEdit. That is unneeded and will cause unforeseen issues, " + + "You installed WorldEdit alongside FastAsyncWorldEdit. That is unneeded and will cause unforeseen issues, " + + "because FastAsyncWorldEdit already provides WorldEdit. " + "Stop your server and delete the 'worldedit-bukkit' jar from your plugins folder."); } } - //FAWE end - //FAWE start + // FAWE end + // FAWE start final Attributes attributes = WorldEditManifest.readAttributes(); Objects.requireNonNull(attributes, "Could not retrieve manifest attributes"); final String type = attributes.getValue("FAWE-Plugin-Jar-Type"); Objects.requireNonNull(type, "Could not determine plugin jar type"); if (PaperLib.isPaper()) { - if (PaperLib.getMinecraftVersion() < 20 || (PaperLib.getMinecraftVersion() == 20 && PaperLib.getMinecraftPatchVersion() < 5)) { + if (PaperLib.getMinecraftVersion() < 20 + || (PaperLib.getMinecraftVersion() == 20 && PaperLib.getMinecraftPatchVersion() < 5)) { if (type.equals("mojang") && !Refraction.isMojangMapped()) { throw new IllegalStateException( - """ - - ********************************************** - ** You are using the wrong FAWE jar for your Minecraft version. - ** Download the correct FAWE jar from Modrinth: https://modrinth.com/plugin/fastasyncworldedit/ - **********************************************""" - ); + """ + + ********************************************** + ** You are using the wrong FAWE jar for your Minecraft version. + ** Download the correct FAWE jar from Modrinth: https://modrinth.com/plugin/fastasyncworldedit/ + **********************************************"""); } - } else if (PaperLib.getMinecraftVersion() > 20 || (PaperLib.getMinecraftVersion() == 20 && PaperLib.getMinecraftPatchVersion() >= 5)) { + } else if (PaperLib.getMinecraftVersion() > 20 + || (PaperLib.getMinecraftVersion() == 20 && PaperLib.getMinecraftPatchVersion() >= 5)) { if (type.equals("spigot")) { LOGGER.warn( - """ - - ********************************************** - ** You are using the Spigot-mapped FAWE jar on a modern Paper version. - ** This will result in slower first-run times and wasted disk space from plugin remapping. - ** Download the Paper FAWE jar from Modrinth to avoid this: https://modrinth.com/plugin/fastasyncworldedit/ - **********************************************""" - ); + """ + + ********************************************** + ** You are using the Spigot-mapped FAWE jar on a modern Paper version. + ** This will result in slower first-run times and wasted disk space from plugin remapping. + ** Download the Paper FAWE jar from Modrinth to avoid this: https://modrinth.com/plugin/fastasyncworldedit/ + **********************************************"""); } } } else { if (type.equals("mojang")) { throw new IllegalStateException( - """ - - ********************************************** - ** You are attempting to run the Paper FAWE jar on a Spigot server. - ** Either switch to Paper (https://papermc.io), or download the correct FAWE jar for your platform - ** from Modrinth: https://modrinth.com/plugin/fastasyncworldedit/ - **********************************************""" - ); + """ + + ********************************************** + ** You are attempting to run the Paper FAWE jar on a Spigot server. + ** Either switch to Paper (https://papermc.io), or download the correct FAWE jar for your platform + ** from Modrinth: https://modrinth.com/plugin/fastasyncworldedit/ + **********************************************"""); } } - //FAWE end + // FAWE end INSTANCE = this; - //noinspection ResultOfMethodCallIgnored + // noinspection ResultOfMethodCallIgnored getDataFolder().mkdirs(); - //FAWE start - Migrate from config-legacy to worldedit-config + // FAWE start - Migrate from config-legacy to worldedit-config migrateLegacyConfig(); - //FAWE end + // FAWE end - //FAWE start - Modify WorldEdit config name - config = new BukkitConfiguration(new YAMLProcessor(new File(getDataFolder(), "worldedit-config.yml"), true), this); - // Load config before we say we've loaded platforms as it is used in listeners of the event - // Load config in onLoad to ensure it is loaded before FAWE settings to allow (inelegant) copying of values across + // FAWE start - Modify WorldEdit config name + config = new BukkitConfiguration(new YAMLProcessor(new File(getDataFolder(), "worldedit-config.yml"), true), + this); + // Load config before we say we've loaded platforms as it is used in listeners + // of the event + // Load config in onLoad to ensure it is loaded before FAWE settings to allow + // (inelegant) copying of values across // where needed config.load(); - //FAWE end + // FAWE end WorldEdit worldEdit = WorldEdit.getInstance(); @@ -203,20 +208,20 @@ public void onLoad() { platform = new BukkitServerInterface(this, getServer()); worldEdit.getPlatformManager().register(platform); - //FAWE start - Setup permission attachments + // FAWE start - Setup permission attachments permissionAttachmentManager = new BukkitPermissionAttachmentManager(this); - //FAWE end + // FAWE end - //FAWE start - initialise bukkitConsoleCommandSender later + // FAWE start - initialise bukkitConsoleCommandSender later this.bukkitConsoleCommandSender = new BukkitCommandSender(this, Bukkit.getConsoleSender()); - //FAWE end + // FAWE end Path delChunks = Paths.get(getDataFolder().getPath(), DELCHUNKS_FILE_NAME); if (Files.exists(delChunks)) { ChunkDeleter.runFromFile(delChunks, true); } - //FAWE start - Delete obsolete DummyFawe from pre 1.14 days + // FAWE start - Delete obsolete DummyFawe from pre 1.14 days if (this.getDataFolder().getParentFile().listFiles(file -> { if (file.getName().equals("DummyFawe.jar")) { file.delete(); @@ -226,7 +231,7 @@ public void onLoad() { }).length > 0) { LOGGER.warn("DummyFawe detected and automatically deleted! This file is no longer necessary."); } - //FAWE end + // FAWE end } /** @@ -235,21 +240,25 @@ public void onLoad() { @Override public void onEnable() { + // Initialize FoliaLib for cross-platform scheduler support + FoliaLibHolder.init(this); + // Catch bad things being done by naughty plugins that include // WorldEdit's classes ClassSourceValidator verifier = new ClassSourceValidator(this); verifier.reportMismatches(ImmutableList.of(World.class, CommandManager.class, EditSession.class, Actor.class)); - //FAWE start + // FAWE start new FaweBukkit(this); - //FAWE end + // FAWE end WorldEdit.getInstance().getEventBus().post(new PlatformsRegisteredEvent()); PermissionsResolverManager.initialize(this); // Setup permission resolver // Register CUI - getServer().getMessenger().registerIncomingPluginChannel(this, CUI_PLUGIN_CHANNEL, new CUIChannelListener(this)); + getServer().getMessenger().registerIncomingPluginChannel(this, CUI_PLUGIN_CHANNEL, + new CUIChannelListener(this)); getServer().getMessenger().registerOutgoingPluginChannel(this, CUI_PLUGIN_CHANNEL); // Now we can register events @@ -259,17 +268,20 @@ public void onEnable() { getServer().getPluginManager().registerEvents(new AsyncTabCompleteListener(), this); } - initializeRegistries(); // this creates the objects matching Bukkit's enums - but doesn't fill them with data yet + initializeRegistries(); // this creates the objects matching Bukkit's enums - but doesn't fill them with + // data yet if (Bukkit.getWorlds().isEmpty()) { setupPreWorldData(); - // register this so we can load world-dependent data right as the first world is loading + // register this so we can load world-dependent data right as the first world is + // loading getServer().getPluginManager().registerEvents(new WorldInitListener(), this); } else { - //FAWE start + // FAWE start LOGGER.warn( "Server reload detected. This may cause various issues with FastAsyncWorldEdit and dependent plugins. Reloading the server is not advised."); - LOGGER.warn("For more information why reloading is bad, see https://madelinemiller.dev/blog/problem-with-reload/"); - //FAWE end + LOGGER.warn( + "For more information why reloading is bad, see https://madelinemiller.dev/blog/problem-with-reload/"); + // FAWE end try { setupPreWorldData(); // since worlds are loaded already, we can do this now @@ -281,8 +293,8 @@ public void onEnable() { // Enable metrics Metrics m = new Metrics(this, BSTATS_ID); // First introduced in build 349, release 2.5.2 - m.addCustomChart(new SimplePie("residence", () - -> WEManager.weManager().getManagers().toString().contains("residence") ? "Yes" : "No")); + m.addCustomChart(new SimplePie("residence", + () -> WEManager.weManager().getManagers().toString().contains("residence") ? "Yes" : "No")); // Check if we are in a safe environment ServerLib.checkUnsafeForks(); // Check if a new build is available @@ -295,61 +307,71 @@ private void setupPreWorldData() { } private void setupWorldData() { - // datapacks aren't loaded until just before the world is, and bukkit has no event for this + // datapacks aren't loaded until just before the world is, and bukkit has no + // event for this // so the earliest we can do this is in WorldInit setupTags(); - setupBiomes(false); // FAWE - load biomes later. Initialize biomes twice to allow for the registry to be present for - // plugins requiring WE biomes during startup, as well as allowing custom biomes loaded later on to be present in WE. + setupBiomes(false); // FAWE - load biomes later. Initialize biomes twice to allow for the registry + // to be present for + // plugins requiring WE biomes during startup, as well as allowing custom biomes + // loaded later on to be present in WE. ((BukkitImplAdapter) adapter.value().get()).setupFeatures(); WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent(platform)); } - @SuppressWarnings({"deprecation", "unchecked"}) + @SuppressWarnings({ "deprecation", "unchecked" }) private void initializeRegistries() { - // FAWE start - move Biomes to their own method. Initialize biomes twice to allow for the registry to be present for - // plugins requiring WE biomes during startup, as well as allowing custom biomes loaded later on to be present in WE. + // FAWE start - move Biomes to their own method. Initialize biomes twice to + // allow for the registry to be present for + // plugins requiring WE biomes during startup, as well as allowing custom biomes + // loaded later on to be present in WE. setupBiomes(true); // FAWE end /* - - // Block & Item - Registry.MATERIAL.forEach(material -> { - String key = material.getKey().toString(); - if (material.isBlock()) { - BlockType.REGISTRY.register(key, new BlockType(key, blockState -> { - // TODO Use something way less hacky than this. - ParserContext context = new ParserContext(); - context.setPreferringWildcard(true); - context.setTryLegacy(false); - context.setRestricted(false); - try { - FuzzyBlockState state = (FuzzyBlockState) WorldEdit.getInstance().getBlockFactory().parseFromInput( - BukkitAdapter.adapt(blockState.getBlockType()).createBlockData().getAsString(), context - ).toImmutableState(); - BlockState defaultState = blockState.getBlockType().getAllStates().get(0); - for (Map.Entry, Object> propertyObjectEntry : state.getStates().entrySet()) { - //noinspection unchecked - defaultState = defaultState.with((Property) propertyObjectEntry.getKey(), propertyObjectEntry.getValue()); - } - return defaultState; - } catch (InputParseException e) { - LOGGER.warn("Error loading block state for " + key, e); - return blockState; - } - })); - } - if (material.isItem()) { - ItemType.REGISTRY.register(key, new ItemType(key)); - } - } -*/ + * + * // Block & Item + * Registry.MATERIAL.forEach(material -> { + * String key = material.getKey().toString(); + * if (material.isBlock()) { + * BlockType.REGISTRY.register(key, new BlockType(key, blockState -> { + * // TODO Use something way less hacky than this. + * ParserContext context = new ParserContext(); + * context.setPreferringWildcard(true); + * context.setTryLegacy(false); + * context.setRestricted(false); + * try { + * FuzzyBlockState state = (FuzzyBlockState) + * WorldEdit.getInstance().getBlockFactory().parseFromInput( + * BukkitAdapter.adapt(blockState.getBlockType()).createBlockData().getAsString( + * ), context + * ).toImmutableState(); + * BlockState defaultState = blockState.getBlockType().getAllStates().get(0); + * for (Map.Entry, Object> propertyObjectEntry : + * state.getStates().entrySet()) { + * //noinspection unchecked + * defaultState = defaultState.with((Property) + * propertyObjectEntry.getKey(), propertyObjectEntry.getValue()); + * } + * return defaultState; + * } catch (InputParseException e) { + * LOGGER.warn("Error loading block state for " + key, e); + * return blockState; + * } + * })); + * } + * if (material.isItem()) { + * ItemType.REGISTRY.register(key, new ItemType(key)); + * } + * } + */ // Entity for (org.bukkit.entity.EntityType entityType : org.bukkit.entity.EntityType.values()) { String mcid = entityType.getName(); if (mcid != null) { String lowerCaseMcId = mcid.toLowerCase(Locale.ROOT); - EntityType.REGISTRY.register("minecraft:" + lowerCaseMcId, new EntityType("minecraft:" + lowerCaseMcId)); + EntityType.REGISTRY.register("minecraft:" + lowerCaseMcId, + new EntityType("minecraft:" + lowerCaseMcId)); } } // ... :| @@ -377,8 +399,10 @@ private void setupTags() { // FAWE start private void setupBiomes(boolean expectFail) { if (this.adapter.value().isPresent()) { - // Biomes are stored globally in the server. Registries are not kept per-world in Minecraft. - // The WorldServer get-registries method simply delegates to the MinecraftServer method. + // Biomes are stored globally in the server. Registries are not kept per-world + // in Minecraft. + // The WorldServer get-registries method simply delegates to the MinecraftServer + // method. for (final NamespacedKey biome : ((BukkitImplAdapter) adapter.value().get()).getRegisteredBiomes()) { BiomeType biomeType; if ((biomeType = BiomeType.REGISTRY.get(biome.toString())) == null) { // only register once @@ -431,11 +455,11 @@ private void loadAdapter() { if (platform instanceof BukkitServerInterface) { LOGGER.warn(e.getMessage()); } else { - //FAWE start - Identify as FAWE + // FAWE start - Identify as FAWE LOGGER.info("FastAsyncWorldEdit could not find a Bukkit adapter for this MC version, " + "but it seems that you have another implementation of FastAsyncWorldEdit installed ({}) " + "that handles the world editing.", platform.getPlatformName()); - //FAWE end + // FAWE end } this.adapter.invalidate(); } @@ -457,7 +481,8 @@ public void onDisable() { if (config != null) { config.unload(); } - this.getServer().getScheduler().cancelTasks(this); + // Shutdown FoliaLib and cancel all tasks + FoliaLibHolder.shutdown(); } /** @@ -521,8 +546,8 @@ public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, // code of WorldEdit expects it String[] split = new String[args.length + 1]; System.arraycopy(args, 0, split, 1, args.length); - split[0] = commandLabel.startsWith("fastasyncworldedit:") ? commandLabel.replace("fastasyncworldedit:", "") : - commandLabel; + split[0] = commandLabel.startsWith("fastasyncworldedit:") ? commandLabel.replace("fastasyncworldedit:", "") + : commandLabel; CommandEvent event = new CommandEvent(wrapCommandSender(sender), Joiner.on(" ").join(split)); getWorldEdit().getEventBus().post(event); @@ -595,7 +620,7 @@ public PermissionsResolverManager getPermissionsResolver() { return PermissionsResolverManager.getInstance(); } - //FAWE start + // FAWE start /** * Get the permissions attachment manager in use @@ -605,7 +630,7 @@ public PermissionsResolverManager getPermissionsResolver() { public BukkitPermissionAttachmentManager getPermissionAttachmentManager() { return permissionAttachmentManager; } - //FAWE end + // FAWE end /** * Used to wrap a Bukkit Player as a WorldEdit Player. @@ -614,7 +639,7 @@ public BukkitPermissionAttachmentManager getPermissionAttachmentManager() { * @return a wrapped player */ public BukkitPlayer wrapPlayer(Player player) { - //FAWE start - Use cache over returning a direct BukkitPlayer + // FAWE start - Use cache over returning a direct BukkitPlayer BukkitPlayer wePlayer = getCachedPlayer(player); if (wePlayer != null) { return wePlayer; @@ -627,10 +652,10 @@ public BukkitPlayer wrapPlayer(Player player) { } return bukkitPlayer; } - //FAWE end + // FAWE end } - //FAWE start + // FAWE start BukkitPlayer getCachedPlayer(Player player) { List meta = player.getMetadata("WE"); if (meta.isEmpty()) { @@ -646,7 +671,7 @@ BukkitPlayer reCachePlayer(Player player) { return wePlayer; } } - //FAWE end + // FAWE end public Actor wrapCommandSender(CommandSender sender) { if (sender instanceof Player) { @@ -736,14 +761,15 @@ public void onAsyncTabComplete(com.destroystokyo.paper.event.server.AsyncTabComp return; } - final Optional command - = WorldEdit.getInstance().getPlatformManager().getPlatformCommandManager().getCommandManager().getCommand( - label); + final Optional command = WorldEdit.getInstance().getPlatformManager() + .getPlatformCommandManager().getCommandManager().getCommand( + label); if (!command.isPresent()) { return; } - CommandSuggestionEvent suggestEvent = new CommandSuggestionEvent(wrapCommandSender(event.getSender()), buffer); + CommandSuggestionEvent suggestEvent = new CommandSuggestionEvent(wrapCommandSender(event.getSender()), + buffer); getWorldEdit().getEventBus().post(suggestEvent); event.setCompletions(CommandUtil.fixSuggestions(buffer, suggestEvent.getSuggestions())); diff --git a/worldedit-bukkit/src/main/resources/plugin.yml b/worldedit-bukkit/src/main/resources/plugin.yml index 86f95e9ef7..1db84efd09 100644 --- a/worldedit-bukkit/src/main/resources/plugin.yml +++ b/worldedit-bukkit/src/main/resources/plugin.yml @@ -9,6 +9,7 @@ website: https://modrinth.com/plugin/fastasyncworldedit/ description: Blazingly fast world manipulation for builders, large networks and developers. authors: [ Empire92, MattBDev, IronApollo, dordsor21, NotMyFault ] loadbefore: [ WorldGuard, PlotSquared ] +folia-supported: true database: false permissions: fawe.plotsquared: diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java index 57a61d1f0e..81456b42dc 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java @@ -47,33 +47,48 @@ /** * [ WorldEdit action ] *
- * [ EditSession ] - The change is processed (area restrictions, change limit, block type) + * [ EditSession ] - The change is processed (area restrictions, change limit, + * block type) *
* [ Block change ] - A block change from some location *
* [ Set Queue ] - The SetQueue manages the implementation specific queue *
- * [ Fawe Queue] - A queue of chunks - check if the queue has the chunk for a change + * [ Fawe Queue] - A queue of chunks - check if the queue has the chunk for a + * change *
- * [ Fawe Chunk Implementation ] - Otherwise create a new FaweChunk object which is a wrapper around the Chunk object + * [ Fawe Chunk Implementation ] - Otherwise create a new FaweChunk object which + * is a wrapper around the Chunk object *
- * [ Execution ] - When done, the queue then sets the blocks for the chunk, performs lighting updates and sends the chunk packet to the clients + * [ Execution ] - When done, the queue then sets the blocks for the chunk, + * performs lighting updates and sends the chunk packet to the clients *

* Why it's faster: - *
The chunk is modified directly rather than through the API + *
+ * The chunk is modified directly rather than through the API * - Removes some overhead, and means some processing can be done async - *
Lighting updates are performed on the chunk level rather than for every block - * - e.g., A blob of stone: only the visible blocks need to have the lighting calculated - *
Block changes are sent with a chunk packet - * - A chunk packet is generally quicker to create and smaller for large world edits - *
No physics updates + *
+ * Lighting updates are performed on the chunk level rather than for every block + * - e.g., A blob of stone: only the visible blocks need to have the lighting + * calculated + *
+ * Block changes are sent with a chunk packet + * - A chunk packet is generally quicker to create and smaller for large world + * edits + *
+ * No physics updates * - Physics updates are slow, and are usually performed on each block - *
Block data shortcuts - * - Some known blocks don't need to have the data set or accessed (e.g., air is never going to have data) - *
Remove redundant extents + *
+ * Block data shortcuts + * - Some known blocks don't need to have the data set or accessed (e.g., air is + * never going to have data) + *
+ * Remove redundant extents * - Up to 11 layers of extents can be removed - *
History bypassing - * - FastMode bypasses history and means blocks in the world don't need to be checked and recorded + *
+ * History bypassing + * - FastMode bypasses history and means blocks in the world don't need to be + * checked and recorded */ public class Fawe { @@ -81,6 +96,12 @@ public class Fawe { private static Fawe instance; + /** + * Whether the server is running Folia. + * This is set by the platform implementation at startup. + */ + private static boolean isFoliaServer = false; + /** * The ticks-per-second timer. */ @@ -103,8 +124,8 @@ private Fawe(final IFawe implementation) { * Implementation dependent stuff */ this.setupConfigs(); - FaweLimit.MAX.CONFIRM_LARGE = - Settings.settings().LIMITS.get("default").CONFIRM_LARGE || Settings.settings().GENERAL.LIMIT_UNLIMITED_CONFIRMS; + FaweLimit.MAX.CONFIRM_LARGE = Settings.settings().LIMITS.get("default").CONFIRM_LARGE + || Settings.settings().GENERAL.LIMIT_UNLIMITED_CONFIRMS; TaskManager.IMP = this.implementation.getTaskManager(); TaskManager.taskManager().async(() -> { @@ -112,14 +133,12 @@ private Fawe(final IFawe implementation) { MainUtil.getFile(this.implementation .getDirectory(), Settings.settings().PATHS.HISTORY), TimeUnit.DAYS.toMillis(Settings.settings().HISTORY.DELETE_AFTER_DAYS), - false - ); + false); MainUtil.deleteOlder( MainUtil.getFile(this.implementation .getDirectory(), Settings.settings().PATHS.CLIPBOARD), TimeUnit.DAYS.toMillis(Settings.settings().CLIPBOARD.DELETE_AFTER_DAYS), - false - ); + false); }); /* @@ -144,12 +163,12 @@ private Fawe(final IFawe implementation) { 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), - new UUIDKeyQueuedThreadFactory() - )); + new UUIDKeyQueuedThreadFactory())); } /** * Get the implementation specific class. + * * @deprecated use {@link #platform()} */ @SuppressWarnings("unchecked") @@ -160,6 +179,7 @@ public static T imp() { /** * Get the implementation specific class. + * * @since 2.0.0 */ @SuppressWarnings("unchecked") @@ -167,9 +187,9 @@ public static T platform() { return instance != null ? (T) instance.implementation : null; } - /** * Get the implementation independent class. + * * @deprecated use {@link #instance()} */ @Deprecated(forRemoval = true, since = "2.0.0") @@ -185,11 +205,13 @@ public static Fawe instance() { } /** - * This method is not for public use. If you have to ask what it does then you shouldn't be using it. + * This method is not for public use. If you have to ask what it does then you + * shouldn't be using it. */ public static void set(final IFawe implementation) throws InstanceAlreadyExistsException, IllegalArgumentException { if (instance != null) { - throw new InstanceAlreadyExistsException("FAWE has already been initialized with: " + instance.implementation); + throw new InstanceAlreadyExistsException( + "FAWE has already been initialized with: " + instance.implementation); } if (implementation == null) { throw new IllegalArgumentException("Implementation may not be null."); @@ -211,11 +233,36 @@ public static boolean isMainThread() { } /** - * Non-api. Handles an input FAWE exception if not already handled, given the input boolean array. - * Looks at the {@link FaweException.Type} and decides what to do (rethrows if we want to attempt to show the error to the + * Check if the server is running Folia. + * This is set by the platform implementation (e.g., FoliaLibHolder) at startup. + * + * @return true if running on Folia, false otherwise + * @since 2.14.4 + */ + public static boolean isFoliaServer() { + return isFoliaServer; + } + + /** + * Set whether the server is running Folia. + * This should only be called by the platform implementation at startup. + * + * @param isFolia true if running on Folia, false otherwise + * @since 2.14.4 + */ + public static void setFoliaServer(boolean isFolia) { + isFoliaServer = isFolia; + } + + /** + * Non-api. Handles an input FAWE exception if not already handled, given the + * input boolean array. + * Looks at the {@link FaweException.Type} and decides what to do (rethrows if + * we want to attempt to show the error to the * player, outputs to console where necessary). * - * @param faweExceptionReasonsUsed boolean array that should be cached where this method is called from of length {@code + * @param faweExceptionReasonsUsed boolean array that should be cached where + * this method is called from of length {@code * FaweException.Type.values().length} * @param e {@link FaweException} to handle * @param logger {@link Logger} of the calling class @@ -223,8 +270,7 @@ public static boolean isMainThread() { public static void handleFaweException( boolean[] faweExceptionReasonsUsed, FaweException e, - final Logger logger - ) { + final Logger logger) { FaweException.Type type = e.getType(); switch (type) { case OTHER: @@ -278,7 +324,8 @@ public QueueHandler getQueueHandler() { } public TextureUtil getCachedTextureUtil(boolean randomize, int min, int max) { - // TODO NOT IMPLEMENTED - optimize this by caching the default true/0/100 texture util + // TODO NOT IMPLEMENTED - optimize this by caching the default true/0/100 + // texture util TextureUtil tu = getTextureUtil(); try { tu = min == 0 && max == 100 ? tu : new CleanTextureUtil(tu, min, max); @@ -334,7 +381,7 @@ public void setupConfigs() { File file = new File(this.implementation.getDirectory(), "config.yml"); Settings.settings().PLATFORM = implementation.getPlatform().replace("\"", ""); try (InputStream stream = getClass().getResourceAsStream("/fawe.properties"); - BufferedReader br = new BufferedReader(new InputStreamReader(stream))) { + BufferedReader br = new BufferedReader(new InputStreamReader(stream))) { String versionString = br.readLine(); String commitString = br.readLine(); String dateString = br.readLine(); @@ -342,7 +389,8 @@ public void setupConfigs() { this.version = FaweVersion.tryParse(versionString, commitString, dateString); Settings.settings().DATE = new Date(100 + version.year, version.month, version.day).toString(); Settings.settings().BUILD = "https://ci.athion.net/job/FastAsyncWorldEdit/" + version.build; - Settings.settings().COMMIT = "https://github.com/IntellectualSites/FastAsyncWorldEdit/commit/" + Integer.toHexString(version.hash); + Settings.settings().COMMIT = "https://github.com/IntellectualSites/FastAsyncWorldEdit/commit/" + + Integer.toHexString(version.hash); } catch (Throwable ignored) { } try { @@ -352,15 +400,13 @@ public void setupConfigs() { } Settings.settings().QUEUE.TARGET_SIZE = Math.max( Settings.settings().QUEUE.TARGET_SIZE, - Settings.settings().QUEUE.PARALLEL_THREADS - ); + Settings.settings().QUEUE.PARALLEL_THREADS); if (Settings.settings().QUEUE.TARGET_SIZE < 4 * Settings.settings().QUEUE.PARALLEL_THREADS) { LOGGER.error( "queue.target-size is {}, and queue.parallel_threads is {}. It is HIGHLY recommended that queue" + ".target-size be at least four times queue.parallel-threads or greater.", Settings.settings().QUEUE.TARGET_SIZE, - Settings.settings().QUEUE.PARALLEL_THREADS - ); + Settings.settings().QUEUE.PARALLEL_THREADS); } if (Settings.settings().HISTORY.DELETE_DISK_ON_LOGOUT && Settings.settings().HISTORY.USE_DATABASE) { LOGGER.warn("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); @@ -391,9 +437,12 @@ public void setupConfigs() { assert (Zstd.decompress(ob, compressed) == 0); LOGGER.info("ZSTD Compression Binding loaded successfully"); } catch (Throwable e) { - if (Settings.settings().CLIPBOARD.COMPRESSION_LEVEL > 6 || Settings.settings().HISTORY.COMPRESSION_LEVEL > 6) { - Settings.settings().CLIPBOARD.COMPRESSION_LEVEL = Math.min(6, Settings.settings().CLIPBOARD.COMPRESSION_LEVEL); - Settings.settings().HISTORY.COMPRESSION_LEVEL = Math.min(6, Settings.settings().HISTORY.COMPRESSION_LEVEL); + if (Settings.settings().CLIPBOARD.COMPRESSION_LEVEL > 6 + || Settings.settings().HISTORY.COMPRESSION_LEVEL > 6) { + Settings.settings().CLIPBOARD.COMPRESSION_LEVEL = Math.min(6, + Settings.settings().CLIPBOARD.COMPRESSION_LEVEL); + Settings.settings().HISTORY.COMPRESSION_LEVEL = Math.min(6, + Settings.settings().HISTORY.COMPRESSION_LEVEL); LOGGER.error("ZSTD Compression Binding Not Found.\n" + "FAWE will still work but compression won't work as well.", e); } @@ -457,13 +506,17 @@ public Thread setMainThread() { } /** - * Gets the executor used for clipboard IO if clipboard on disk is enabled or null + * Gets the executor used for clipboard IO if clipboard on disk is enabled or + * null * - * @return Executor used for clipboard IO if clipboard on disk is enabled or null + * @return Executor used for clipboard IO if clipboard on disk is enabled or + * null * @since 2.6.2 * @deprecated Use any of {@link Fawe#submitUUIDKeyQueuedTask(UUID, Runnable)}, - * {@link Fawe#submitUUIDKeyQueuedTask(UUID, Runnable, Object)}, {@link Fawe#submitUUIDKeyQueuedTask(UUID, Callable)} - * to ensure if a thread is already a UUID-queued thread, the task is immediately run + * {@link Fawe#submitUUIDKeyQueuedTask(UUID, Runnable, Object)}, + * {@link Fawe#submitUUIDKeyQueuedTask(UUID, Callable)} + * to ensure if a thread is already a UUID-queued thread, the task + * is immediately run */ @Deprecated(forRemoval = true, since = "2.12.1") public KeyQueuedExecutorService getClipboardExecutor() { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java index e5f43ddbcb..2e3a06347f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java @@ -40,11 +40,12 @@ /** * Class which handles all the queues {@link IQueueExtent} */ -@SuppressWarnings({"unchecked", "rawtypes"}) +@SuppressWarnings({ "unchecked", "rawtypes" }) public abstract class QueueHandler implements Trimable, Runnable { /** - * Primary queue should be used for tasks that are unlikely to wait on other tasks, IO, etc. (i.e. spend most of their + * Primary queue should be used for tasks that are unlikely to wait on other + * tasks, IO, etc. (i.e. spend most of their * time utilising CPU. */ private final ForkJoinPool forkJoinPoolPrimary = new ForkJoinPool( @@ -57,39 +58,43 @@ public abstract class QueueHandler implements Trimable, Runnable { 0, pool -> true, 60, - TimeUnit.SECONDS - ); + TimeUnit.SECONDS); /** - * Secondary queue should be used for "cleanup" tasks that are likely to be shorter in life than those submitted to the + * Secondary queue should be used for "cleanup" tasks that are likely to be + * shorter in life than those submitted to the * primary queue. They may be IO-bound tasks. */ private final ForkJoinPool forkJoinPoolSecondary = new ForkJoinPool( Settings.settings().QUEUE.PARALLEL_THREADS, new FaweForkJoinWorkerThreadFactory("FAWE Fork Join Pool Secondary - %s"), null, - false - ); + false); /** - * Main "work-horse" queue for FAWE. Handles chunk submission (and chunk submission alone). Blocking in order to forcibly + * Main "work-horse" queue for FAWE. Handles chunk submission (and chunk + * submission alone). Blocking in order to forcibly * prevent overworking/over-submission of chunk process tasks. */ private final ThreadPoolExecutor blockingExecutor = FaweCache.INSTANCE.newBlockingExecutor( "FAWE QueueHandler Blocking Executor - %d"); /** - * Queue for tasks to be completed on the main thread. These take priority of tasks submitted to syncWhenFree queue + * Queue for tasks to be completed on the main thread. These take priority of + * tasks submitted to syncWhenFree queue */ private final ConcurrentLinkedQueue syncTasks = new ConcurrentLinkedQueue<>(); /** - * Queue for tasks to be completed on the main thread. These are completed only if and when there is time left in a tick + * Queue for tasks to be completed on the main thread. These are completed only + * if and when there is time left in a tick * after completing all tasks in the syncTasks queue */ private final ConcurrentLinkedQueue syncWhenFree = new ConcurrentLinkedQueue<>(); private final Map>> chunkGetCache = new HashMap<>(); - private final CleanableThreadLocal> queuePool = new CleanableThreadLocal<>(QueueHandler.this::create); + private final CleanableThreadLocal> queuePool = new CleanableThreadLocal<>( + QueueHandler.this::create); /** - * Used to calculate elapsed time in milliseconds and ensure block placement doesn't lag the + * Used to calculate elapsed time in milliseconds and ensure block placement + * doesn't lag the * server */ private long last; @@ -106,7 +111,7 @@ public ThreadPoolExecutor getBlockingExecutor() { @Override public void run() { - if (!Fawe.isMainThread()) { + if (!Fawe.isMainThread() && !Fawe.isFoliaServer()) { throw new IllegalStateException("Not main thread"); } if (!syncTasks.isEmpty()) { @@ -127,7 +132,8 @@ public void run() { } /** - * Get if the {@code blockingExecutor} is saturated with tasks or not. Under-utilisation implies the queue has space for + * Get if the {@code blockingExecutor} is saturated with tasks or not. + * Under-utilisation implies the queue has space for * more submissions. * * @return true if {@code blockingExecutor} is not saturated with tasks @@ -192,8 +198,10 @@ public > void complete(Future task) { } /** - * Complete a task in the {@code forkJoinPoolSecondary} queue. Secondary queue should be used for "cleanup" tasks that are - * likely to be shorter in life than those submitted to the primary queue. They may be IO-bound tasks. + * Complete a task in the {@code forkJoinPoolSecondary} queue. Secondary queue + * should be used for "cleanup" tasks that are + * likely to be shorter in life than those submitted to the primary queue. They + * may be IO-bound tasks. * * @param run Runnable to run * @param value Value to return when done @@ -205,8 +213,10 @@ public Future async(Runnable run, T value) { } /** - * Complete a task in the {@code forkJoinPoolSecondary} queue. Secondary queue should be used for "cleanup" tasks that are - * likely to be shorter in life than those submitted to the primary queue. They may be IO-bound tasks. + * Complete a task in the {@code forkJoinPoolSecondary} queue. Secondary queue + * should be used for "cleanup" tasks that are + * likely to be shorter in life than those submitted to the primary queue. They + * may be IO-bound tasks. * * @param run Runnable to run * @return Future for submitted task @@ -216,8 +226,10 @@ public Future async(Runnable run) { } /** - * Complete a task in the {@code forkJoinPoolSecondary} queue. Secondary queue should be used for "cleanup" tasks that are - * likely to be shorter in life than those submitted to the primary queue. They may be IO-bound tasks. + * Complete a task in the {@code forkJoinPoolSecondary} queue. Secondary queue + * should be used for "cleanup" tasks that are + * likely to be shorter in life than those submitted to the primary queue. They + * may be IO-bound tasks. * * @param call Callable to run * @param Return value type @@ -228,7 +240,8 @@ public Future async(Callable call) { } /** - * Complete a task in the {@code forkJoinPoolPrimary} queue. Primary queue should be used for tasks that are unlikely to + * Complete a task in the {@code forkJoinPoolPrimary} queue. Primary queue + * should be used for tasks that are unlikely to * wait on other tasks, IO, etc. (i.e. spend most of their time utilising CPU. * * @param run Task to run @@ -239,7 +252,8 @@ public ForkJoinTask submit(Runnable run) { } /** - * Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to + * Submit a task to be run on the main thread. Does not guarantee to be run on + * the next tick as FAWE will only operate to * maintain approx. 18 tps. * * @param run Task to run @@ -251,7 +265,8 @@ public Future sync(Runnable run) { } /** - * Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to + * Submit a task to be run on the main thread. Does not guarantee to be run on + * the next tick as FAWE will only operate to * maintain approx. 18 tps. * * @param call Task to run @@ -263,7 +278,8 @@ public Future sync(Callable call) throws Exception { } /** - * Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to + * Submit a task to be run on the main thread. Does not guarantee to be run on + * the next tick as FAWE will only operate to * maintain approx. 18 tps. * * @param supplier Task to run @@ -275,9 +291,12 @@ public Future sync(Supplier supplier) { } /** - * Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to - * maintain approx. 18 tps. Takes lower priority than tasks submitted via any {@code QueueHandler#sync} method. Completed - * only if and when there is time left in a tick after completing all sync tasks submitted using the aforementioned methods. + * Submit a task to be run on the main thread. Does not guarantee to be run on + * the next tick as FAWE will only operate to + * maintain approx. 18 tps. Takes lower priority than tasks submitted via any + * {@code QueueHandler#sync} method. Completed + * only if and when there is time left in a tick after completing all sync tasks + * submitted using the aforementioned methods. * * @param run Task to run * @param value Value to return when done @@ -289,9 +308,12 @@ public Future syncWhenFree(Runnable run, T value) { } /** - * Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to - * maintain approx. 18 tps. Takes lower priority than tasks submitted via any {@code QueueHandler#sync} method. Completed - * only if and when there is time left in a tick after completing all sync tasks submitted using the aforementioned methods. + * Submit a task to be run on the main thread. Does not guarantee to be run on + * the next tick as FAWE will only operate to + * maintain approx. 18 tps. Takes lower priority than tasks submitted via any + * {@code QueueHandler#sync} method. Completed + * only if and when there is time left in a tick after completing all sync tasks + * submitted using the aforementioned methods. * * @param run Task to run * @param Value type @@ -302,9 +324,12 @@ public Future syncWhenFree(Runnable run) { } /** - * Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to - * maintain approx. 18 tps. Takes lower priority than tasks submitted via any {@code QueueHandler#sync} method. Completed - * only if and when there is time left in a tick after completing all sync tasks submitted using the aforementioned methods. + * Submit a task to be run on the main thread. Does not guarantee to be run on + * the next tick as FAWE will only operate to + * maintain approx. 18 tps. Takes lower priority than tasks submitted via any + * {@code QueueHandler#sync} method. Completed + * only if and when there is time left in a tick after completing all sync tasks + * submitted using the aforementioned methods. * * @param call Task to run * @param Value type @@ -315,9 +340,12 @@ public Future syncWhenFree(Callable call) throws Exception { } /** - * Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to - * maintain approx. 18 tps. Takes lower priority than tasks submitted via any {@code QueueHandler#sync} method. Completed - * only if and when there is time left in a tick after completing all sync tasks submitted using the aforementioned methods. + * Submit a task to be run on the main thread. Does not guarantee to be run on + * the next tick as FAWE will only operate to + * maintain approx. 18 tps. Takes lower priority than tasks submitted via any + * {@code QueueHandler#sync} method. Completed + * only if and when there is time left in a tick after completing all sync tasks + * submitted using the aforementioned methods. * * @param supplier Task to run * @param Value type @@ -328,7 +356,7 @@ public Future syncWhenFree(Supplier supplier) { } private Future sync(Runnable run, T value, Queue queue) { - if (Fawe.isMainThread()) { + if (Fawe.isMainThread() && !Fawe.isFoliaServer()) { run.run(); return Futures.immediateFuture(value); } @@ -339,7 +367,7 @@ private Future sync(Runnable run, T value, Queue queue) { } private Future sync(Runnable run, Queue queue) { - if (Fawe.isMainThread()) { + if (Fawe.isMainThread() && !Fawe.isFoliaServer()) { run.run(); return Futures.immediateCancelledFuture(); } @@ -350,7 +378,7 @@ private Future sync(Runnable run, Queue queue) { } private Future sync(Callable call, Queue queue) throws Exception { - if (Fawe.isMainThread()) { + if (Fawe.isMainThread() && !Fawe.isFoliaServer()) { return Futures.immediateFuture(call.call()); } final FutureTask result = new FutureTask<>(call); @@ -360,7 +388,7 @@ private Future sync(Callable call, Queue queue) throws Exc } private Future sync(Supplier call, Queue queue) { - if (Fawe.isMainThread()) { + if (Fawe.isMainThread() && !Fawe.isFoliaServer()) { return Futures.immediateFuture(call.get()); } final FutureTask result = new FutureTask<>(call::get); @@ -376,8 +404,10 @@ private void notifySync(Object object) { } /** - * Internal use only. Specifically for submitting {@link IQueueChunk} for "processing" an edit. Submits to the blocking - * executor, the main "work-horse" queue for FAWE. Handles chunk submission (and chunk submission alone). Blocking in order + * Internal use only. Specifically for submitting {@link IQueueChunk} for + * "processing" an edit. Submits to the blocking + * executor, the main "work-horse" queue for FAWE. Handles chunk submission (and + * chunk submission alone). Blocking in order * to forcibly prevent overworking/over-submission of chunk process tasks. * * @param chunk chunk @@ -385,9 +415,9 @@ private void notifySync(Object object) { * @return Future representing task */ public > T submit(IQueueChunk chunk) { -// if (MemUtil.isMemoryFree()) { TODO NOT IMPLEMENTED - optimize this -// return (T) forkJoinPoolSecondary.submit(chunk); -// } + // if (MemUtil.isMemoryFree()) { TODO NOT IMPLEMENTED - optimize this + // return (T) forkJoinPoolSecondary.submit(chunk); + // } return (T) blockingExecutor.submit(chunk); } @@ -421,7 +451,8 @@ public IQueueExtent create() { } /** - * Sets the current thread's {@link IQueueExtent} instance in the queue pool to null. + * Sets the current thread's {@link IQueueExtent} instance in the queue pool to + * null. */ public void unCache() { queuePool.remove(); @@ -439,19 +470,20 @@ private IQueueExtent pool() { * Indicate a "set" task is being started. * * @param parallel if the "set" being started is parallel/async - * @deprecated To be replaced by better-named {@link QueueHandler#startUnsafe(boolean)} )} + * @deprecated To be replaced by better-named + * {@link QueueHandler#startUnsafe(boolean)} )} */ @Deprecated(forRemoval = true, since = "2.6.2") public void startSet(boolean parallel) { startUnsafe(parallel); } - /** * Indicate a "set" task is ending. * * @param parallel if the "set" being started is parallel/async - * @deprecated To be replaced by better-named {@link QueueHandler#endUnsafe(boolean)} )} + * @deprecated To be replaced by better-named + * {@link QueueHandler#endUnsafe(boolean)} )} */ @Deprecated(forRemoval = true, since = "2.6.2") public void endSet(boolean parallel) { @@ -459,14 +491,16 @@ public void endSet(boolean parallel) { } /** - * Indicate an unsafe task is starting. Physics are frozen, async catchers disabled, etc. for the duration of the task + * Indicate an unsafe task is starting. Physics are frozen, async catchers + * disabled, etc. for the duration of the task * * @param parallel If the task is being run async and/or in parallel */ public abstract void startUnsafe(boolean parallel); /** - * Indicate a/the unsafe task submitted after a {@link QueueHandler#startUnsafe(boolean)} call has ended. + * Indicate a/the unsafe task submitted after a + * {@link QueueHandler#startUnsafe(boolean)} call has ended. * * @param parallel If the task was being run async and/or in parallel */ @@ -528,7 +562,8 @@ public boolean trim(boolean aggressive) { } /** - * Primary queue should be used for tasks that are unlikely to wait on other tasks, IO, etc. (i.e. spend most of their + * Primary queue should be used for tasks that are unlikely to wait on other + * tasks, IO, etc. (i.e. spend most of their * time utilising CPU. *

* Internal API usage only. @@ -540,7 +575,8 @@ public ExecutorService getForkJoinPoolPrimary() { } /** - * Secondary queue should be used for "cleanup" tasks that are likely to be shorter in life than those submitted to the + * Secondary queue should be used for "cleanup" tasks that are likely to be + * shorter in life than those submitted to the * primary queue. They may be IO-bound tasks. *

* Internal API usage only. diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java index 176e02673b..bc4a3f5249 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java @@ -89,7 +89,8 @@ public ForkJoinPool getPublicForkJoinPool() { /** * Run a bunch of tasks in parallel using the shared thread pool. * - * @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do. + * @deprecated Deprecated without replacement as unused internally, and poor + * implementation of what it's designed to do. */ @Deprecated(forRemoval = true, since = "2.7.0") public void parallel(Collection runables) { @@ -104,7 +105,8 @@ public void parallel(Collection runables) { * * @param runnables the tasks to run * @param numThreads number of threads (null = config.yml parallel threads) - * @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do. + * @deprecated Deprecated without replacement as unused internally, and poor + * implementation of what it's designed to do. */ @Deprecated(forRemoval = true, since = "2.7.0") public void parallel(Collection runnables, @Nullable Integer numThreads) { @@ -223,7 +225,6 @@ public void taskSoonMain(@Nonnull final Runnable runnable, boolean async) { } } - /** * Run a task later on the main thread. * @@ -276,7 +277,8 @@ public void run() { } /** - * @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do. + * @deprecated Deprecated without replacement as unused internally, and poor + * implementation of what it's designed to do. */ @Deprecated(forRemoval = true, since = "2.7.0") public void wait(AtomicBoolean running, int timeout) { @@ -286,7 +288,8 @@ public void wait(AtomicBoolean running, int timeout) { while (running.get()) { running.wait(timeout); if (running.get() && System.currentTimeMillis() - start > 60000) { - new RuntimeException("FAWE is taking a long time to execute a task (might just be a symptom): ").printStackTrace(); + new RuntimeException("FAWE is taking a long time to execute a task (might just be a symptom): ") + .printStackTrace(); LOGGER.info("For full debug information use: /fawe threads"); } } @@ -297,7 +300,8 @@ public void wait(AtomicBoolean running, int timeout) { } /** - * @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do. + * @deprecated Deprecated without replacement as unused internally, and poor + * implementation of what it's designed to do. */ @Deprecated(forRemoval = true, since = "2.7.0") public void notify(AtomicBoolean running) { @@ -316,12 +320,14 @@ public void taskWhenFree(@Nonnull Runnable run) { } /** - * Run a task on the main thread when the TPS is high enough, and wait for execution to finish. - * - Useful if you need to access something from the Bukkit API from another thread
+ * Run a task on the main thread when the TPS is high enough, and wait for + * execution to finish. + * - Useful if you need to access something from the Bukkit API from another + * thread
* - Usually wait time is around 25ms
*/ public T syncWhenFree(@Nonnull final RunnableVal function) { - if (Fawe.isMainThread()) { + if (Fawe.isMainThread() || Fawe.isFoliaServer()) { function.run(); return function.value; } @@ -333,12 +339,14 @@ public T syncWhenFree(@Nonnull final RunnableVal function) { } /** - * Run a task on the main thread when the TPS is high enough, and wait for execution to finish. - * - Useful if you need to access something from the Bukkit API from another thread
+ * Run a task on the main thread when the TPS is high enough, and wait for + * execution to finish. + * - Useful if you need to access something from the Bukkit API from another + * thread
* - Usually wait time is around 25ms
*/ public T syncWhenFree(@Nonnull final Supplier supplier) { - if (Fawe.isMainThread()) { + if (Fawe.isMainThread() || Fawe.isFoliaServer()) { return supplier.get(); } try { @@ -350,7 +358,8 @@ public T syncWhenFree(@Nonnull final Supplier supplier) { /** * Quickly run a task on the main thread, and wait for execution to finish. - * - Useful if you need to access something from the Bukkit API from another thread
+ * - Useful if you need to access something from the Bukkit API from another + * thread
* - Usually wait time is around 25ms */ public T sync(@Nonnull final RunnableVal function) { @@ -359,11 +368,12 @@ public T sync(@Nonnull final RunnableVal function) { /** * Quickly run a task on the main thread, and wait for execution to finish. - * - Useful if you need to access something from the Bukkit API from another thread
+ * - Useful if you need to access something from the Bukkit API from another + * thread
* - Usually wait time is around 25ms
*/ public T sync(final Supplier function) { - if (Fawe.isMainThread()) { + if (Fawe.isMainThread() || Fawe.isFoliaServer()) { return function.get(); } try { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java index 8421df551c..948d5f8c86 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java @@ -1,5 +1,6 @@ package com.fastasyncworldedit.core.wrappers; +import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; @@ -213,14 +214,14 @@ public String getNameUnsafe() { } @Override - public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws - WorldEditException { + public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) + throws WorldEditException { return parent.setBlock(position, block, notifyAndLight); } @Override - public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws - WorldEditException { + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) + throws WorldEditException { return parent.setBlock(position, block, sideEffects); } @@ -262,6 +263,9 @@ public void run(Object value) { @Override public Collection getBlockDrops(final BlockVector3 position) { + if (Fawe.isFoliaServer()) { + return parent.getBlockDrops(position); + } return TaskManager.taskManager().sync(() -> parent.getBlockDrops(position)); } @@ -281,8 +285,8 @@ public boolean regenerate(Region region, Extent extent, RegenOptions options) { } @Override - public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 position) throws - MaxChangedBlocksException { + public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 position) + throws MaxChangedBlocksException { try { return parent.generateTree(type, editSession, position); } catch (MaxChangedBlocksException e) { @@ -291,12 +295,14 @@ public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession } @Override - public boolean generateStructure(final StructureType type, final EditSession editSession, final BlockVector3 position) { + public boolean generateStructure(final StructureType type, final EditSession editSession, + final BlockVector3 position) { return parent.generateStructure(type, editSession, position); } @Override - public boolean generateFeature(final ConfiguredFeatureType type, final EditSession editSession, final BlockVector3 position) { + public boolean generateFeature(final ConfiguredFeatureType type, final EditSession editSession, + final BlockVector3 position) { return parent.generateFeature(type, editSession, position); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 377e32e35f..6010ca9106 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.extension.platform; +import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.function.pattern.PatternTraverser; import com.fastasyncworldedit.core.internal.exception.FaweException; @@ -72,7 +73,9 @@ * Manages registered {@link Platform}s for WorldEdit. Platforms are * implementations of WorldEdit. * - *

This class is thread-safe.

+ *

+ * This class is thread-safe. + *

*/ public class PlatformManager { @@ -124,8 +127,7 @@ public synchronized void register(Platform platform) { "Multiple ports of WorldEdit are installed but they report different versions ({} and {}). " + "If these two versions are truly different, then you may run into unexpected crashes and errors.", firstSeenVersion, - platform.getVersion() - ); + platform.getVersion()); } } else { firstSeenVersion = platform.getVersion(); @@ -135,8 +137,10 @@ public synchronized void register(Platform platform) { /** * Unregister a platform from WorldEdit. * - *

If the platform has been chosen for any capabilities, then a new - * platform will be found.

+ *

+ * If the platform has been chosen for any capabilities, then a new + * platform will be found. + *

* * @param platform the platform */ @@ -171,7 +175,8 @@ public synchronized boolean unregister(Platform platform) { } /** - * Get the preferred platform for handling a certain capability. Throws if none are available. + * Get the preferred platform for handling a certain capability. Throws if none + * are available. * * @param capability the capability * @return the platform @@ -184,12 +189,11 @@ public synchronized Platform queryCapability(Capability capability) throws NoCap } else { if (preferences.isEmpty()) { // Not all platforms registered, this is being called too early! - //FAWE start - exchange WE -> FAWE + // FAWE start - exchange WE -> FAWE throw new NoCapablePlatformException( "Not all platforms have been registered yet!" - + " Please wait until FastAsyncWorldEdit is initialized." - ); - //FAWE end + + " Please wait until FastAsyncWorldEdit is initialized."); + // FAWE end } throw new NoCapablePlatformException("No platform was found supporting " + capability.name()); } @@ -247,7 +251,9 @@ private synchronized Platform findMostPreferred(Capability capability) { /** * Get a list of loaded platforms. * - *

The returned list is a copy of the original and is mutable.

+ *

+ * The returned list is a copy of the original and is mutable. + *

* * @return a list of platforms */ @@ -299,11 +305,11 @@ public T createProxyActor(T base) { } } - //FAWE start + // FAWE start private T proxyFawe(T player) { return (T) new LocationMaskedPlayerWrapper(player, player.getLocation(), true); } - //FAWE end + // FAWE end /** * Get the command manager. @@ -332,8 +338,10 @@ private static ListeningExecutorService createExecutor() { /** * Get the current configuration. * - *

If no platform has been registered yet, then a default configuration - * will be returned.

+ *

+ * If no platform has been registered yet, then a default configuration + * will be returned. + *

* * @return the configuration */ @@ -360,8 +368,10 @@ public boolean isInitialized() { } /** - * You shouldn't have been calling this anyways, but this is now deprecated. Either don't - * fire this event at all, or fire the new event via the event bus if you're a platform. + * You shouldn't have been calling this anyways, but this is now deprecated. + * Either don't + * fire this event at all, or fire the new event via the event bus if you're a + * platform. */ @Deprecated public void handlePlatformReady(@SuppressWarnings("unused") PlatformReadyEvent event) { @@ -429,17 +439,19 @@ public void handleBlockInteract(BlockInteractEvent event) { try { if (event.getType() == Interaction.HIT) { - // superpickaxe is special because its primary interaction is a left click, not a right click - // in addition, it is implicitly bound to all pickaxe items, not just a single tool item + // superpickaxe is special because its primary interaction is a left click, not + // a right click + // in addition, it is implicitly bound to all pickaxe items, not just a single + // tool item if (session.hasSuperPickAxe() && player.isHoldingPickAxe()) { final BlockTool superPickaxe = session.getSuperPickaxe(); if (superPickaxe != null && superPickaxe.canUse(player)) { - //FAWE start - run async + // FAWE start - run async player.runAction(() -> reset(superPickaxe) .actPrimary(queryCapability(Capability.WORLD_EDITING), - getConfiguration(), player, session, location, event.getFace() - ), false, true); - //FAWE end + getConfiguration(), player, session, location, event.getFace()), + false, !Fawe.isFoliaServer()); + // FAWE end event.setCancelled(true); return; } @@ -447,17 +459,17 @@ public void handleBlockInteract(BlockInteractEvent event) { Tool tool = session.getTool(player); if (tool instanceof DoubleActionBlockTool && tool.canUse(player)) { - //FAWE start - run async + // FAWE start - run async player.runAction(() -> reset((DoubleActionBlockTool) tool) .actSecondary(queryCapability(Capability.WORLD_EDITING), - getConfiguration(), player, session, location, event.getFace() - ), false, true); - //FAWE end + getConfiguration(), player, session, location, event.getFace()), + false, !Fawe.isFoliaServer()); + // FAWE end event.setCancelled(true); } } else if (event.getType() == Interaction.OPEN) { - //FAWE start - get general tool over item in main hand & run async + // FAWE start - get general tool over item in main hand & run async Tool tool = session.getTool(player); if (tool instanceof BlockTool && tool.canUse(player)) { if (player.checkAction()) { @@ -468,10 +480,9 @@ public void handleBlockInteract(BlockInteractEvent event) { blockTool = reset(blockTool); } blockTool.actPrimary(queryCapability(Capability.WORLD_EDITING), - getConfiguration(), player, session, location, event.getFace() - ); - }, false, true); - //FAWE end + getConfiguration(), player, session, location, event.getFace()); + }, false, !Fawe.isFoliaServer()); + // FAWE end event.setCancelled(true); } } @@ -483,18 +494,18 @@ public void handleBlockInteract(BlockInteractEvent event) { } } - //FAWE start + // FAWE start public void handleThrowable(Throwable e, Actor actor) { FaweException faweException = FaweException.get(e); if (faweException != null) { actor.print(Caption.of("fawe.cancel.reason", faweException.getComponent())); } else { actor.print(Caption.of("worldedit.command.error.report")); - actor.print(TextComponent.of(e.getClass().getName()+ ": " + e.getMessage())); + actor.print(TextComponent.of(e.getClass().getName() + ": " + e.getMessage())); LOGGER.error("Error occurred executing player action", e); } } - //FAWE end + // FAWE end @Subscribe public void handlePlayerInput(PlayerInputEvent event) { @@ -508,12 +519,17 @@ public void handlePlayerInput(PlayerInputEvent event) { case PRIMARY: { Tool tool = session.getTool(player); if (tool instanceof DoubleActionTraceTool && tool.canUse(player)) { - //FAWE start - run async - player.runAsyncIfFree(() -> reset((DoubleActionTraceTool) tool) - .actSecondary(queryCapability(Capability.WORLD_EDITING), - getConfiguration(), player, session - )); - //FAWE end + // FAWE start - run async + if (Fawe.isFoliaServer()) { + player.runIfFree(() -> reset((DoubleActionTraceTool) tool) + .actSecondary(queryCapability(Capability.WORLD_EDITING), + getConfiguration(), player, session)); + } else { + player.runAsyncIfFree(() -> reset((DoubleActionTraceTool) tool) + .actSecondary(queryCapability(Capability.WORLD_EDITING), + getConfiguration(), player, session)); + } + // FAWE end event.setCancelled(true); return; } @@ -524,12 +540,14 @@ public void handlePlayerInput(PlayerInputEvent event) { case SECONDARY: { Tool tool = session.getTool(player); if (tool instanceof TraceTool && tool.canUse(player)) { - //FAWE start - run async - //todo this needs to be fixed so the event is canceled after actPrimary is used and returns true - player.runAction(() -> reset((TraceTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), - getConfiguration(), player, session - ), false, true); - //FAWE end + // FAWE start - run async + // todo this needs to be fixed so the event is canceled after actPrimary is used + // and returns true + player.runAction( + () -> reset((TraceTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), + getConfiguration(), player, session), + false, true); + // FAWE end event.setCancelled(true); return; } @@ -537,14 +555,13 @@ public void handlePlayerInput(PlayerInputEvent event) { break; } } - //FAWE start - add own message + // FAWE start - add own message } catch (Throwable e) { handleThrowable(e, player); - //FAWE end + // FAWE end } finally { Request.reset(); } } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SurvivalModeExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SurvivalModeExtent.java index b2670113a1..e6542e1a95 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SurvivalModeExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SurvivalModeExtent.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.extent.world; +import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.task.RunnableVal; import com.sk89q.worldedit.WorldEditException; @@ -37,10 +38,12 @@ /** * Makes changes to the world as if a player had done so during survival mode. * - *

Note that this extent may choose to not call the underlying + *

+ * Note that this extent may choose to not call the underlying * extent and may instead call methods on the {@link World} that is passed * in the constructor. For that reason, if you wish to "catch" changes, you - * should catch them before the changes reach this extent.

+ * should catch them before the changes reach this extent. + *

*/ public class SurvivalModeExtent extends AbstractDelegateExtent { @@ -64,9 +67,11 @@ public SurvivalModeExtent(Extent extent, World world) { * Return whether changes to the world should be simulated with the * use of game tools (such as pickaxes) whenever possible and reasonable. * - *

For example, we could pretend that the act of setting a coal ore block + *

+ * For example, we could pretend that the act of setting a coal ore block * to air (nothing) was the act of a player mining that coal ore block - * with a pickaxe, which would mean that a coal item would be dropped.

+ * with a pickaxe, which would mean that a coal item would be dropped. + *

* * @return true if tool use is to be simulated */ @@ -99,6 +104,12 @@ public > boolean setBlock(BlockVector3 location, B Collection drops = world.getBlockDrops(location); boolean canSet = super.setBlock(location, block); if (canSet) { + if (Fawe.isFoliaServer()) { + for (BaseItemStack stack : drops) { + world.dropItem(location.toVector3(), stack); + } + return true; + } TaskManager.taskManager().sync(new RunnableVal<>() { @Override public void run(Object value) {