From 6648b87a62bc250a232fec775e3715a9a08129bd Mon Sep 17 00:00:00 2001 From: Octavia Togami Date: Thu, 5 Sep 2024 02:58:33 -0700 Subject: [PATCH 1/4] Implement chunk section editing --- .../main/kotlin/buildlogic.adapter.gradle.kts | 2 + config/checkstyle/checkstyle-suppression.xml | 4 +- config/checkstyle/checkstyle.xml | 2 +- gradle.properties | 2 +- .../impl/v1_21_3/PaperweightAdapter.java | 95 +++--- .../v1_21_3/PaperweightWorldNativeAccess.java | 187 ------------ .../impl/v1_21_3/StaticRefraction.java | 15 + .../wna/PaperweightNativeBlockState.java | 51 ++++ .../v1_21_3/wna/PaperweightNativeChunk.java | 219 ++++++++++++++ .../wna/PaperweightNativeChunkSection.java | 53 ++++ .../v1_21_3/wna/PaperweightNativeWorld.java | 167 +++++++++++ .../impl/v1_21_4/PaperweightAdapter.java | 37 ++- .../v1_21_4/PaperweightWorldNativeAccess.java | 187 ------------ .../impl/v1_21_4/StaticRefraction.java | 33 +++ .../wna/PaperweightNativeBlockState.java | 51 ++++ .../v1_21_4/wna/PaperweightNativeChunk.java | 219 ++++++++++++++ .../wna/PaperweightNativeChunkSection.java | 53 ++++ .../v1_21_4/wna/PaperweightNativeWorld.java | 167 +++++++++++ worldedit-bukkit/build.gradle.kts | 2 - .../sk89q/worldedit/bukkit/BukkitWorld.java | 22 +- .../bukkit/adapter/BukkitImplAdapter.java | 6 +- .../java/com/sk89q/worldedit/EditSession.java | 30 +- .../sk89q/worldedit/LocalConfiguration.java | 1 + .../internal/SectionBufferingExtent.java | 280 ++++++++++++++++++ .../util/collection/ChunkSectionMask.java | 107 +++++++ .../worldedit/internal/wna/NativeAdapter.java | 34 +++ .../internal/wna/NativeBlockState.java | 33 +++ .../worldedit/internal/wna/NativeChunk.java | 71 +++++ .../internal/wna/NativeChunkSection.java | 68 +++++ .../internal/wna/NativePosition.java | 31 ++ .../worldedit/internal/wna/NativeWorld.java | 56 ++++ .../worldedit/internal/wna/WNASharedImpl.java | 204 +++++++++++++ .../internal/wna/WorldNativeAccess.java | 200 ------------- .../worldedit/internal/wna/package-info.java | 3 +- .../sk89q/worldedit/math/BlockVector3.java | 4 +- .../util/PropertiesConfiguration.java | 1 + .../worldedit/util/YAMLConfiguration.java | 2 + .../sk89q/worldedit/world/AbstractWorld.java | 8 + .../worldedit/world/block/BaseBlock.java | 6 +- .../sk89q/worldedit/fabric/FabricAdapter.java | 24 ++ .../sk89q/worldedit/fabric/FabricWorld.java | 14 +- .../internal/FabricWorldNativeAccess.java | 164 ---------- .../fabric/mixin/MixinNativeBlockState.java | 63 ++++ .../fabric/mixin/MixinNativeChunk.java | 183 ++++++++++++ .../fabric/mixin/MixinNativeChunkSection.java | 82 +++++ .../fabric/mixin/MixinNativePosition.java | 47 +++ .../fabric/mixin/MixinNativeWorld.java | 137 +++++++++ .../resources/worldedit-fabric.mixins.json | 7 +- .../main/resources/worldedit.accesswidener | 5 + .../worldedit/neoforge/NeoForgeAdapter.java | 23 ++ .../worldedit/neoforge/NeoForgeWorld.java | 14 +- .../internal/NeoForgeWorldNativeAccess.java | 173 ----------- .../neoforge/mixin/MixinNativeBlockState.java | 63 ++++ .../neoforge/mixin/MixinNativeChunk.java | 183 ++++++++++++ .../mixin/MixinNativeChunkSection.java | 82 +++++ .../neoforge/mixin/MixinNativePosition.java | 47 +++ .../neoforge/mixin/MixinNativeWorld.java | 137 +++++++++ .../resources/META-INF/accesstransformer.cfg | 5 + .../resources/worldedit-neoforge.mixins.json | 7 +- .../sk89q/worldedit/sponge/SpongeWorld.java | 5 - .../config/ConfigurateConfiguration.java | 2 + .../internal/SpongeWorldNativeAccess.java | 158 ---------- 62 files changed, 3182 insertions(+), 1156 deletions(-) delete mode 100644 worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/PaperweightWorldNativeAccess.java create mode 100644 worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeBlockState.java create mode 100644 worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunk.java create mode 100644 worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunkSection.java create mode 100644 worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeWorld.java delete mode 100644 worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightWorldNativeAccess.java create mode 100644 worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeBlockState.java create mode 100644 worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunk.java create mode 100644 worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunkSection.java create mode 100644 worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeWorld.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/internal/SectionBufferingExtent.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/collection/ChunkSectionMask.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeAdapter.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeBlockState.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeChunk.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeChunkSection.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativePosition.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeWorld.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WNASharedImpl.java delete mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java delete mode 100644 worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricWorldNativeAccess.java create mode 100644 worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeBlockState.java create mode 100644 worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeChunk.java create mode 100644 worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeChunkSection.java create mode 100644 worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativePosition.java create mode 100644 worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeWorld.java delete mode 100644 worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/internal/NeoForgeWorldNativeAccess.java create mode 100644 worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeBlockState.java create mode 100644 worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeChunk.java create mode 100644 worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeChunkSection.java create mode 100644 worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativePosition.java create mode 100644 worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeWorld.java delete mode 100644 worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/SpongeWorldNativeAccess.java diff --git a/build-logic/src/main/kotlin/buildlogic.adapter.gradle.kts b/build-logic/src/main/kotlin/buildlogic.adapter.gradle.kts index 576b9d832e..14c8723bb7 100644 --- a/build-logic/src/main/kotlin/buildlogic.adapter.gradle.kts +++ b/build-logic/src/main/kotlin/buildlogic.adapter.gradle.kts @@ -1,3 +1,4 @@ +import buildlogic.getLibrary import buildlogic.stringyLibs import buildlogic.getVersion @@ -25,6 +26,7 @@ repositories { dependencies { "implementation"(project(":worldedit-bukkit")) + "implementation"(stringyLibs.getLibrary("paperLib")) constraints { "remapper"("net.fabricmc:tiny-remapper:[${stringyLibs.getVersion("minimumTinyRemapper")},)") { because("Need remapper to support Java 21") diff --git a/config/checkstyle/checkstyle-suppression.xml b/config/checkstyle/checkstyle-suppression.xml index 8f37b5bec9..13e951be27 100644 --- a/config/checkstyle/checkstyle-suppression.xml +++ b/config/checkstyle/checkstyle-suppression.xml @@ -9,8 +9,8 @@ - - + + diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 4c533604de..962e210cf2 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -184,7 +184,7 @@ Checks based on Google Checks, modified for EngineHub. --> - + diff --git a/gradle.properties b/gradle.properties index f3b8605355..cde5cfcb0c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ group=com.sk89q.worldedit version=7.4.0-SNAPSHOT -org.gradle.jvmargs=-Xmx1500M +org.gradle.jvmargs=-Xmx1700M org.gradle.parallel=true loom_fabric_repository=https://maven.enginehub.org/artifactory/fabricmc/ diff --git a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/PaperweightAdapter.java index 30adb5b05b..13174e373d 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/PaperweightAdapter.java @@ -34,12 +34,17 @@ import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_3.wna.PaperweightNativeBlockState; +import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_3.wna.PaperweightNativeWorld; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extension.platform.Watchdog; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; -import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.internal.wna.NativeAdapter; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.internal.wna.NativeWorld; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; @@ -171,7 +176,6 @@ import org.spigotmc.SpigotConfig; import org.spigotmc.WatchdogThread; -import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -199,9 +203,28 @@ import static com.google.common.base.Preconditions.checkState; public final class PaperweightAdapter implements BukkitImplAdapter { + public static BlockPos adaptPos(NativePosition pos) { + return new BlockPos(pos.x(), pos.y(), pos.z()); + } private final Logger logger = Logger.getLogger(getClass().getCanonicalName()); + private final NativeAdapter nativeAdapter = new NativeAdapter() { + @Override + public NativeBlockState toNative(BlockState state) { + return new PaperweightNativeBlockState(adapt(state)); + } + + @Override + public BlockState fromNative(NativeBlockState state) { + return adapt(((PaperweightNativeBlockState) state).delegate()); + } + + @Override public NativePosition newBlockPos(BlockVector3 pos) { + return pos; + } + }; + private final Field serverWorldsField; private final Method getChunkFutureMethod; private final Field chunkProviderExecutorField; @@ -259,6 +282,10 @@ public PaperweightAdapter() throws NoSuchFieldException, NoSuchMethodException { } } + public NativeAdapter asNativeAdapter() { + return nativeAdapter; + } + @Override public DataFixer getDataFixer() { return this.dataFixer; @@ -270,7 +297,7 @@ public DataFixer getDataFixer() { * @param tileEntity the tile entity * @param tag the tag */ - static void readTagIntoTileEntity(net.minecraft.nbt.CompoundTag tag, BlockEntity tileEntity) { + static void readTagIntoTileEntity(CompoundTag tag, BlockEntity tileEntity) { tileEntity.loadWithComponents(tag, MinecraftServer.getServer().registryAccess()); tileEntity.setChanged(); } @@ -291,7 +318,7 @@ private static String getEntityId(Entity entity) { * @param entity the entity * @param tag the tag */ - private static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTag tag) { + private static void readEntityIntoTag(Entity entity, CompoundTag tag) { entity.save(tag); } @@ -375,7 +402,7 @@ public BaseBlock getFullBlock(Location location) { // Read the NBT data BlockEntity te = chunk.getBlockEntity(blockPos); if (te != null) { - net.minecraft.nbt.CompoundTag tag = te.saveWithId(MinecraftServer.getServer().registryAccess()); + CompoundTag tag = te.saveWithId(MinecraftServer.getServer().registryAccess()); return state.toBaseBlock(LazyReference.from(() -> (LinCompoundTag) toNative(tag))); } @@ -417,8 +444,8 @@ public void setBiome(Location location, BiomeType biome) { } @Override - public WorldNativeAccess createWorldNativeAccess(World world) { - return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle())); + public NativeWorld createNativeInterface(World world) { + return new PaperweightNativeWorld(this, ((CraftWorld) world).getHandle()); } private static net.minecraft.core.Direction adapt(Direction face) { @@ -479,7 +506,7 @@ public BaseEntity getEntity(org.bukkit.entity.Entity entity) { String id = getEntityId(mcEntity); - net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); + CompoundTag tag = new CompoundTag(); readEntityIntoTag(mcEntity, tag); return new BaseEntity( EntityTypes.get(id), @@ -499,12 +526,12 @@ public org.bukkit.entity.Entity createEntity(Location location, BaseEntity state String entityId = state.getType().id(); LinCompoundTag nativeTag = state.getNbt(); - net.minecraft.nbt.CompoundTag tag; + CompoundTag tag; if (nativeTag != null) { - tag = (net.minecraft.nbt.CompoundTag) fromNative(nativeTag); + tag = (CompoundTag) fromNative(nativeTag); removeUnwantedEntityTagsRecursively(tag); } else { - tag = new net.minecraft.nbt.CompoundTag(); + tag = new CompoundTag(); } tag.putString("id", entityId); @@ -523,14 +550,14 @@ public org.bukkit.entity.Entity createEntity(Location location, BaseEntity state } // This removes all unwanted tags from the main entity and all its passengers - private void removeUnwantedEntityTagsRecursively(net.minecraft.nbt.CompoundTag tag) { + private void removeUnwantedEntityTagsRecursively(CompoundTag tag) { for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { tag.remove(name); } // Adapted from net.minecraft.world.entity.EntityType#loadEntityRecursive if (tag.contains("Passengers", LinTagId.LIST.id())) { - net.minecraft.nbt.ListTag nbttaglist = tag.getList("Passengers", LinTagId.COMPOUND.id()); + ListTag nbttaglist = tag.getList("Passengers", LinTagId.COMPOUND.id()); for (int i = 0; i < nbttaglist.size(); ++i) { removeUnwantedEntityTagsRecursively(nbttaglist.getCompound(i)); @@ -610,7 +637,7 @@ public void sendFakeNBT(Player player, BlockVector3 pos, LinCompoundTag nbtData) structureBlock.setLevel(((CraftPlayer) player).getHandle().level()); ((CraftPlayer) player).getHandle().connection.send(ClientboundBlockEntityDataPacket.create( structureBlock, - (blockEntity, registryAccess) -> (net.minecraft.nbt.CompoundTag) fromNative(nbtData) + (blockEntity, registryAccess) -> (CompoundTag) fromNative(nbtData) )); } @@ -834,7 +861,7 @@ private void regenForWorld(Region region, Extent extent, ServerLevel serverWorld Objects.requireNonNull(state); BlockEntity blockEntity = chunk.getBlockEntity(pos); if (blockEntity != null) { - net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId(serverWorld.registryAccess()); + CompoundTag tag = blockEntity.saveWithId(serverWorld.registryAccess()); state = state.toBaseBlock(LazyReference.from(() -> (LinCompoundTag) toNative(tag))); } extent.setBlock(vec, state.toBaseBlock()); @@ -997,44 +1024,44 @@ public void sendBiomeUpdates(World world, Iterable chunks) { * @param foreign non-native NMS NBT structure * @return native WorldEdit NBT structure */ - LinTag toNative(net.minecraft.nbt.Tag foreign) { + public LinTag toNative(Tag foreign) { if (foreign == null) { return null; } - if (foreign instanceof net.minecraft.nbt.CompoundTag compoundTag) { + if (foreign instanceof CompoundTag compoundTag) { LinCompoundTag.Builder builder = LinCompoundTag.builder(); for (var entry : compoundTag.getAllKeys()) { builder.put(entry, toNative(compoundTag.get(entry))); } return builder.build(); - } else if (foreign instanceof net.minecraft.nbt.ByteTag byteTag) { + } else if (foreign instanceof ByteTag byteTag) { return LinByteTag.of(byteTag.getAsByte()); - } else if (foreign instanceof net.minecraft.nbt.ByteArrayTag byteArrayTag) { + } else if (foreign instanceof ByteArrayTag byteArrayTag) { return LinByteArrayTag.of(byteArrayTag.getAsByteArray()); - } else if (foreign instanceof net.minecraft.nbt.DoubleTag doubleTag) { + } else if (foreign instanceof DoubleTag doubleTag) { return LinDoubleTag.of(doubleTag.getAsDouble()); - } else if (foreign instanceof net.minecraft.nbt.FloatTag floatTag) { + } else if (foreign instanceof FloatTag floatTag) { return LinFloatTag.of(floatTag.getAsFloat()); - } else if (foreign instanceof net.minecraft.nbt.IntTag intTag) { + } else if (foreign instanceof IntTag intTag) { return LinIntTag.of(intTag.getAsInt()); - } else if (foreign instanceof net.minecraft.nbt.IntArrayTag intArrayTag) { + } else if (foreign instanceof IntArrayTag intArrayTag) { return LinIntArrayTag.of(intArrayTag.getAsIntArray()); - } else if (foreign instanceof net.minecraft.nbt.LongArrayTag longArrayTag) { + } else if (foreign instanceof LongArrayTag longArrayTag) { return LinLongArrayTag.of(longArrayTag.getAsLongArray()); - } else if (foreign instanceof net.minecraft.nbt.ListTag listTag) { + } else if (foreign instanceof ListTag listTag) { try { return toNativeList(listTag); } catch (Throwable e) { logger.log(Level.WARNING, "Failed to convert net.minecraft.nbt.ListTag", e); return LinListTag.empty(LinTagType.endTag()); } - } else if (foreign instanceof net.minecraft.nbt.LongTag longTag) { + } else if (foreign instanceof LongTag longTag) { return LinLongTag.of(longTag.getAsLong()); - } else if (foreign instanceof net.minecraft.nbt.ShortTag shortTag) { + } else if (foreign instanceof ShortTag shortTag) { return LinShortTag.of(shortTag.getAsShort()); - } else if (foreign instanceof net.minecraft.nbt.StringTag stringTag) { + } else if (foreign instanceof StringTag stringTag) { return LinStringTag.of(stringTag.getAsString()); - } else if (foreign instanceof net.minecraft.nbt.EndTag) { + } else if (foreign instanceof EndTag) { return LinEndTag.instance(); } else { throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName()); @@ -1049,12 +1076,12 @@ LinTag toNative(net.minecraft.nbt.Tag foreign) { * @throws SecurityException on error * @throws IllegalArgumentException on error */ - private LinListTag toNativeList(net.minecraft.nbt.ListTag foreign) throws SecurityException, IllegalArgumentException { + private LinListTag toNativeList(ListTag foreign) throws SecurityException, IllegalArgumentException { LinListTag.Builder> builder = LinListTag.builder( LinTagType.fromId(LinTagId.fromId(foreign.getElementType())) ); - for (net.minecraft.nbt.Tag tag : foreign) { + for (Tag tag : foreign) { builder.add(toNative(tag)); } @@ -1067,12 +1094,12 @@ private LinListTag toNativeList(net.minecraft.nbt.ListTag foreign) throws Sec * @param foreign structure to convert * @return non-native structure */ - Tag fromNative(LinTag foreign) { + public Tag fromNative(LinTag foreign) { if (foreign == null) { return null; } if (foreign instanceof LinCompoundTag compoundTag) { - net.minecraft.nbt.CompoundTag tag = new CompoundTag(); + CompoundTag tag = new CompoundTag(); for (var entry : compoundTag.value().entrySet()) { tag.put(entry.getKey(), fromNative(entry.getValue())); } @@ -1092,7 +1119,7 @@ Tag fromNative(LinTag foreign) { } else if (foreign instanceof LinLongArrayTag longArrayTag) { return new LongArrayTag(longArrayTag.value()); } else if (foreign instanceof LinListTag listTag) { - net.minecraft.nbt.ListTag tag = new ListTag(); + ListTag tag = new ListTag(); for (var t : listTag.value()) { tag.add(fromNative(t)); } diff --git a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/PaperweightWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/PaperweightWorldNativeAccess.java deleted file mode 100644 index 70b7786a10..0000000000 --- a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/PaperweightWorldNativeAccess.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.bukkit.adapter.impl.v1_21_3; - -import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.internal.block.BlockStateIdAccess; -import com.sk89q.worldedit.internal.wna.WorldNativeAccess; -import com.sk89q.worldedit.util.SideEffect; -import com.sk89q.worldedit.util.SideEffectSet; -import com.sk89q.worldedit.world.block.BlockState; -import net.minecraft.core.BlockPos; -import net.minecraft.nbt.Tag; -import net.minecraft.server.level.FullChunkStatus; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.block.Block; -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.craftbukkit.CraftWorld; -import org.bukkit.craftbukkit.block.data.CraftBlockData; -import org.bukkit.event.block.BlockPhysicsEvent; -import org.enginehub.linbus.tree.LinCompoundTag; - -import java.lang.ref.WeakReference; -import java.util.Objects; -import javax.annotation.Nullable; - -public class PaperweightWorldNativeAccess implements WorldNativeAccess { - private static final int UPDATE = 1; - private static final int NOTIFY = 2; - - private final PaperweightAdapter adapter; - private final WeakReference world; - private SideEffectSet sideEffectSet; - - public PaperweightWorldNativeAccess(PaperweightAdapter adapter, WeakReference world) { - this.adapter = adapter; - this.world = world; - } - - private ServerLevel getWorld() { - return Objects.requireNonNull(world.get(), "The reference to the world was lost"); - } - - @Override - public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { - this.sideEffectSet = sideEffectSet; - } - - @Override - public LevelChunk getChunk(int x, int z) { - return getWorld().getChunk(x, z); - } - - @Override - public net.minecraft.world.level.block.state.BlockState toNative(BlockState state) { - int stateId = BlockStateIdAccess.getBlockStateId(state); - return BlockStateIdAccess.isValidInternalId(stateId) - ? Block.stateById(stateId) - : ((CraftBlockData) BukkitAdapter.adapt(state)).getState(); - } - - @Override - public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk chunk, BlockPos position) { - return chunk.getBlockState(position); - } - - @Nullable - @Override - public net.minecraft.world.level.block.state.BlockState setBlockState(LevelChunk chunk, BlockPos position, net.minecraft.world.level.block.state.BlockState state) { - return chunk.setBlockState(position, state, false, this.sideEffectSet.shouldApply(SideEffect.UPDATE)); - } - - @Override - public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(net.minecraft.world.level.block.state.BlockState block, BlockPos position) { - return Block.updateFromNeighbourShapes(block, getWorld(), position); - } - - @Override - public BlockPos getPosition(int x, int y, int z) { - return new BlockPos(x, y, z); - } - - @Override - public void updateLightingForBlock(BlockPos position) { - getWorld().getChunkSource().getLightEngine().checkBlock(position); - } - - @Override - public boolean updateTileEntity(BlockPos position, LinCompoundTag tag) { - // We will assume that the tile entity was created for us - BlockEntity tileEntity = getWorld().getBlockEntity(position); - if (tileEntity == null) { - return false; - } - Tag nativeTag = adapter.fromNative(tag); - PaperweightAdapter.readTagIntoTileEntity((net.minecraft.nbt.CompoundTag) nativeTag, tileEntity); - return true; - } - - @Override - public void notifyBlockUpdate(LevelChunk chunk, BlockPos position, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { - if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { - getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY); - } - } - - @Override - public boolean isChunkTicking(LevelChunk chunk) { - return chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING); - } - - @Override - public void markBlockChanged(LevelChunk chunk, BlockPos position) { - if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { - getWorld().getChunkSource().blockChanged(position); - } - } - - @Override - public void notifyNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { - ServerLevel world = getWorld(); - if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { - world.updateNeighborsAt(pos, oldState.getBlock()); - } else { - // When we don't want events, manually run the physics without them. - Block block = oldState.getBlock(); - fireNeighborChanged(pos, world, block, pos.west()); - fireNeighborChanged(pos, world, block, pos.east()); - fireNeighborChanged(pos, world, block, pos.below()); - fireNeighborChanged(pos, world, block, pos.above()); - fireNeighborChanged(pos, world, block, pos.north()); - fireNeighborChanged(pos, world, block, pos.south()); - } - if (newState.hasAnalogOutputSignal()) { - world.updateNeighbourForOutputSignal(pos, newState.getBlock()); - } - } - - @Override - public void updateBlock(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { - ServerLevel world = getWorld(); - newState.onPlace(world, pos, oldState, false); - } - - private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) { - world.getBlockState(neighborPos).handleNeighborChanged(world, neighborPos, block, ExperimentalRedstoneUtils.initialOrientation(world, null, null), false); - } - - @Override - public void updateNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState, int recursionLimit) { - ServerLevel world = getWorld(); - oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); - if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { - CraftWorld craftWorld = world.getWorld(); - BlockPhysicsEvent event = new BlockPhysicsEvent(craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), CraftBlockData.fromData(newState)); - world.getCraftServer().getPluginManager().callEvent(event); - if (event.isCancelled()) { - return; - } - } - newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit); - newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); - } - - @Override - public void onBlockStateChange(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { - getWorld().onBlockStateChange(pos, oldState, newState); - } -} diff --git a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/StaticRefraction.java b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/StaticRefraction.java index 56b11e71ba..83f2fb47d6 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/StaticRefraction.java +++ b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/StaticRefraction.java @@ -88,4 +88,19 @@ public final class StaticRefraction { public static final String DESTROY_BLOCK_BREAKING_ENTITY_MAX_UPDATE = Refraction.pickName( "destroyBlock", "a" ); + public static final String GET_VISIBLE_CHUNK_IF_PRESENT = Refraction.pickName( + "getVisibleChunkIfPresent", "b" + ); + public static final String CHANGED_BLOCKS_PER_SECTION = Refraction.pickName( + "changedBlocksPerSection", "n" + ); + public static final String HAS_CHANGED_SECTIONS = Refraction.pickName( + "hasChangedSections", "m" + ); + /** + * {@code updateBlockEntityTicker(BlockEntity blockEntity)}. + */ + public static final String UPDATE_BLOCK_ENTITY_TICKER = Refraction.pickName( + "updateBlockEntityTicker", "c" + ); } diff --git a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeBlockState.java b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeBlockState.java new file mode 100644 index 0000000000..08f85c476e --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeBlockState.java @@ -0,0 +1,51 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.impl.v1_21_3.wna; + +import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_3.PaperweightAdapter; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +public record PaperweightNativeBlockState(BlockState delegate) implements NativeBlockState { + @Override + public boolean isSame(NativeBlockState other) { + return this.delegate == ((PaperweightNativeBlockState) other).delegate; + } + + @Override + public boolean isSameBlockType(NativeBlockState other) { + return delegate.getBlock() == ((PaperweightNativeBlockState) other).delegate.getBlock(); + } + + @Override + public boolean hasBlockEntity() { + return delegate.hasBlockEntity(); + } + + @Override + public NativeBlockState updateFromNeighbourShapes(NativeWorld world, NativePosition position) { + return new PaperweightNativeBlockState(Block.updateFromNeighbourShapes( + delegate, ((PaperweightNativeWorld) world).delegate(), PaperweightAdapter.adaptPos(position) + )); + } +} diff --git a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunk.java b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunk.java new file mode 100644 index 0000000000..0192e4b3aa --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunk.java @@ -0,0 +1,219 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.impl.v1_21_3.wna; + +import com.google.common.base.Preconditions; +import com.google.common.base.Throwables; +import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_3.PaperweightAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_3.StaticRefraction; +import com.sk89q.worldedit.internal.util.collection.ChunkSectionMask; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativeChunk; +import com.sk89q.worldedit.internal.wna.NativeChunkSection; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import com.sk89q.worldedit.internal.wna.WNASharedImpl; +import com.sk89q.worldedit.math.BlockVector3; +import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet; +import it.unimi.dsi.fastutil.shorts.ShortSet; +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.FullChunkStatus; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.levelgen.Heightmap; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.EnumSet; +import java.util.Set; +import javax.annotation.Nullable; + +public record PaperweightNativeChunk(NativeWorld owner, LevelChunk delegate) implements NativeChunk { + private static final MethodHandle GET_VISIBLE_CHUNK_IF_PRESENT; + private static final MethodHandle GET_CHANGED_BLOCKS_PER_SECTION; + private static final MethodHandle SET_HAS_CHANGED_SECTIONS; + private static final MethodHandle UPDATE_BLOCK_ENTITY_TICKER; + + static { + try { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + Method m = ServerChunkCache.class.getDeclaredMethod( + StaticRefraction.GET_VISIBLE_CHUNK_IF_PRESENT, long.class + ); + m.setAccessible(true); + GET_VISIBLE_CHUNK_IF_PRESENT = lookup.unreflect(m); + + Field f = ChunkHolder.class.getDeclaredField(StaticRefraction.CHANGED_BLOCKS_PER_SECTION); + f.setAccessible(true); + GET_CHANGED_BLOCKS_PER_SECTION = lookup.unreflectGetter(f); + + f = ChunkHolder.class.getDeclaredField(StaticRefraction.HAS_CHANGED_SECTIONS); + f.setAccessible(true); + SET_HAS_CHANGED_SECTIONS = lookup.unreflectSetter(f); + + m = LevelChunk.class.getDeclaredMethod(StaticRefraction.UPDATE_BLOCK_ENTITY_TICKER, BlockEntity.class); + m.setAccessible(true); + UPDATE_BLOCK_ENTITY_TICKER = lookup.unreflect(m); + } catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException e) { + throw new AssertionError(e); + } + } + + private static final Set HEIGHTMAPS = EnumSet.of( + Heightmap.Types.WORLD_SURFACE, + Heightmap.Types.OCEAN_FLOOR, + Heightmap.Types.MOTION_BLOCKING, + Heightmap.Types.MOTION_BLOCKING_NO_LEAVES + ); + + @Override + public NativeWorld getWorld() { + return owner; + } + + @Override + public boolean isTicking() { + return delegate.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING); + } + + @Override + public NativePosition getWorldPos(int offsetX, int offsetY, int offsetZ) { + ChunkPos pos = delegate.getPos(); + return new BlockVector3(pos.getBlockX(offsetX), offsetY, pos.getBlockZ(offsetZ)); + } + + @Override + public NativeBlockState getBlockState(NativePosition blockPos) { + return new PaperweightNativeBlockState(delegate.getBlockState(PaperweightAdapter.adaptPos(blockPos))); + } + + @Override + public @Nullable NativeBlockState setBlockState(NativePosition blockPos, NativeBlockState newState, boolean update) { + return new PaperweightNativeBlockState(delegate.setBlockState( + PaperweightAdapter.adaptPos(blockPos), + ((PaperweightNativeBlockState) newState).delegate(), + false, + update + )); + } + + @Override + public void markSectionChanged(int index, ChunkSectionMask changed) { + ServerChunkCache serverChunkCache = (ServerChunkCache) delegate.getLevel().getChunkSource(); + ChunkHolder holder; + try { + holder = (ChunkHolder) GET_VISIBLE_CHUNK_IF_PRESENT.invoke( + serverChunkCache, + delegate.getPos().toLong() + ); + } catch (Throwable e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + if (holder != null) { + ShortSet[] changedBlocksPerSection; + try { + changedBlocksPerSection = (ShortSet[]) GET_CHANGED_BLOCKS_PER_SECTION.invoke(holder); + } catch (Throwable e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + if (changedBlocksPerSection[index] == null) { + try { + SET_HAS_CHANGED_SECTIONS.invoke(holder, true); + } catch (Throwable e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + changedBlocksPerSection[index] = new ShortOpenHashSet(changed.asShortCollection()); + } else { + changedBlocksPerSection[index].addAll(changed.asShortCollection()); + } + // Trick to get the holder into the broadcast set + serverChunkCache.onChunkReadyToSend(holder); + } + } + + @Override + public void updateHeightmaps() { + Heightmap.primeHeightmaps(delegate, HEIGHTMAPS); + } + + @Override + public void updateLightingForSectionAirChange(int index, boolean onlyAir) { + delegate.getLevel().getLightEngine().updateSectionStatus( + SectionPos.of(delegate.getPos(), delegate.getLevel().getSectionYFromSectionIndex(index)), + onlyAir + ); + } + + @Override + public void removeSectionBlockEntity(int chunkX, int chunkY, int chunkZ) { + delegate.removeBlockEntity(delegate.getPos().getBlockAt(chunkX, chunkY, chunkZ)); + } + + @SuppressWarnings("deprecation") + @Override + public void initializeBlockEntity(int chunkX, int chunkY, int chunkZ, NativeBlockState newState) { + BlockPos pos = delegate.getPos().getBlockAt(chunkX, chunkY, chunkZ); + BlockEntity blockEntity = delegate.getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK); + BlockState nativeState = ((PaperweightNativeBlockState) newState).delegate(); + if (blockEntity == null) { + blockEntity = ((EntityBlock) nativeState.getBlock()).newBlockEntity(pos, nativeState); + if (blockEntity != null) { + delegate.addAndRegisterBlockEntity(blockEntity); + } + } else { + blockEntity.setBlockState(nativeState); + try { + UPDATE_BLOCK_ENTITY_TICKER.invoke(blockEntity); + } catch (Throwable e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + } + } + + @Override + public NativeChunkSection getChunkSection(int index) { + return new PaperweightNativeChunkSection(delegate.getSection(index)); + } + + @Override + public NativeChunkSection setChunkSection(int index, NativeChunkSection section, ChunkSectionMask modifiedBlocks) { + Preconditions.checkPositionIndex(index, delegate.getSectionsCount()); + LevelChunkSection[] chunkSections = delegate.getSections(); + var oldSection = new PaperweightNativeChunkSection(chunkSections[index]); + chunkSections[index] = ((PaperweightNativeChunkSection) section).delegate(); + WNASharedImpl.postChunkSectionReplacement(this, index, oldSection, section, modifiedBlocks); + delegate.markUnsaved(); + return oldSection; + } +} diff --git a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunkSection.java b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunkSection.java new file mode 100644 index 0000000000..3e90d2463e --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunkSection.java @@ -0,0 +1,53 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.impl.v1_21_3.wna; + +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativeChunkSection; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunkSection; + +public record PaperweightNativeChunkSection(LevelChunkSection delegate) implements NativeChunkSection { + @Override + public boolean isOnlyAir() { + return delegate.hasOnlyAir(); + } + + @Override + public NativeBlockState getThenSetBlock(int i, int j, int k, NativeBlockState blockState) { + BlockState nativeState = ((PaperweightNativeBlockState) blockState).delegate(); + if (isOnlyAir() && nativeState.isAir()) { + return blockState; + } + return new PaperweightNativeBlockState(delegate.setBlockState(i, j, k, nativeState, false)); + } + + @Override + public NativeBlockState getBlock(int i, int j, int k) { + return new PaperweightNativeBlockState(delegate.getBlockState(i, j, k)); + } + + @Override + public NativeChunkSection copy() { + return new PaperweightNativeChunkSection(new LevelChunkSection( + delegate.getStates().copy(), delegate.getBiomes().copy() + )); + } +} diff --git a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeWorld.java b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeWorld.java new file mode 100644 index 0000000000..fd71597f7c --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeWorld.java @@ -0,0 +1,167 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.impl.v1_21_3.wna; + +import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_3.PaperweightAdapter; +import com.sk89q.worldedit.internal.wna.NativeAdapter; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativeChunk; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.profiling.Profiler; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.event.block.BlockPhysicsEvent; +import org.enginehub.linbus.tree.LinCompoundTag; + +public record PaperweightNativeWorld(PaperweightAdapter adapter, ServerLevel delegate) implements NativeWorld { + @Override + public NativeAdapter getAdapter() { + return adapter.asNativeAdapter(); + } + + @Override + public int getSectionIndex(int y) { + return delegate.getSectionIndex(y); + } + + @Override + public int getYForSectionIndex(int index) { + return SectionPos.sectionToBlockCoord(delegate.getSectionYFromSectionIndex(index)); + } + + @Override + public NativeChunk getChunk(int chunkX, int chunkZ) { + return new PaperweightNativeChunk(this, delegate.getChunk(chunkX, chunkZ)); + } + + @Override + public void notifyBlockUpdate(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { + delegate.sendBlockUpdated( + PaperweightAdapter.adaptPos(pos), + ((PaperweightNativeBlockState) oldState).delegate(), + ((PaperweightNativeBlockState) newState).delegate(), + Block.UPDATE_NEIGHBORS | Block.UPDATE_CLIENTS + ); + } + + @Override + public void markBlockChanged(NativePosition pos) { + delegate.getChunkSource().blockChanged(PaperweightAdapter.adaptPos(pos)); + } + + @Override + public void updateLightingForBlock(NativePosition position) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("updateSkyLightSources"); + /* Paper removes the update here */ + profilerFiller.popPush("queueCheckLight"); + delegate.getChunkSource().getLightEngine().checkBlock(PaperweightAdapter.adaptPos(position)); + profilerFiller.pop(); + } + + @Override + public boolean updateTileEntity(NativePosition position, LinCompoundTag tag) { + CompoundTag nativeTag = (CompoundTag) adapter.fromNative(tag); + BlockPos nativePos = PaperweightAdapter.adaptPos(position); + BlockEntity tileEntity = delegate.getChunkAt(nativePos).getBlockEntity(nativePos); + if (tileEntity == null) { + return false; + } + tileEntity.loadWithComponents(nativeTag, delegate.registryAccess()); + tileEntity.setChanged(); + return true; + } + + @Override + public void notifyNeighbors( + NativePosition pos, NativeBlockState oldState, NativeBlockState newState, boolean events + ) { + BlockPos nativePos = PaperweightAdapter.adaptPos(pos); + if (events) { + delegate.updateNeighborsAt(nativePos, ((PaperweightNativeBlockState) oldState).delegate().getBlock()); + } else { + // When we don't want events, manually run the physics without them. + Block block = ((PaperweightNativeBlockState) oldState).delegate().getBlock(); + fireNeighborChanged(nativePos, delegate, block, nativePos.west()); + fireNeighborChanged(nativePos, delegate, block, nativePos.east()); + fireNeighborChanged(nativePos, delegate, block, nativePos.below()); + fireNeighborChanged(nativePos, delegate, block, nativePos.above()); + fireNeighborChanged(nativePos, delegate, block, nativePos.north()); + fireNeighborChanged(nativePos, delegate, block, nativePos.south()); + } + BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate(); + if (nativeNewState.hasAnalogOutputSignal()) { + delegate.updateNeighbourForOutputSignal(nativePos, nativeNewState.getBlock()); + } + } + + private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) { + world.getBlockState(neighborPos).handleNeighborChanged(world, neighborPos, block, null, false); + } + + @Override + public void updateBlock(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { + BlockPos nativePos = PaperweightAdapter.adaptPos(pos); + BlockState nativeOldState = ((PaperweightNativeBlockState) oldState).delegate(); + BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate(); + nativeOldState.onRemove(delegate, nativePos, nativeNewState, false); + nativeNewState.onPlace(delegate, nativePos, nativeOldState, false); + } + + @Override + public void updateNeighbors( + NativePosition pos, NativeBlockState oldState, NativeBlockState newState, int recursionLimit, boolean events + ) { + BlockPos nativePos = PaperweightAdapter.adaptPos(pos); + BlockState nativeOldState = ((PaperweightNativeBlockState) oldState).delegate(); + BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate(); + nativeOldState.updateIndirectNeighbourShapes(delegate, nativePos, Block.UPDATE_CLIENTS, recursionLimit); + if (events) { + BlockPhysicsEvent event = new BlockPhysicsEvent( + CraftBlock.at(delegate, nativePos), + CraftBlockData.fromData(nativeNewState) + ); + delegate.getCraftServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + } + nativeNewState.updateNeighbourShapes(delegate, nativePos, Block.UPDATE_CLIENTS, recursionLimit); + nativeNewState.updateIndirectNeighbourShapes(delegate, nativePos, Block.UPDATE_CLIENTS, recursionLimit); + } + + @Override + public void onBlockStateChange(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { + delegate.onBlockStateChange( + PaperweightAdapter.adaptPos(pos), + ((PaperweightNativeBlockState) oldState).delegate(), + ((PaperweightNativeBlockState) newState).delegate() + ); + } +} diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java index 59b193b3f6..c86bad6f76 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java @@ -35,12 +35,17 @@ import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_4.wna.PaperweightNativeBlockState; +import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_4.wna.PaperweightNativeWorld; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extension.platform.Watchdog; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; -import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.internal.wna.NativeAdapter; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.internal.wna.NativeWorld; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; @@ -171,7 +176,6 @@ import org.spigotmc.SpigotConfig; import org.spigotmc.WatchdogThread; -import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -199,9 +203,28 @@ import static com.google.common.base.Preconditions.checkState; public final class PaperweightAdapter implements BukkitImplAdapter { + public static BlockPos adaptPos(NativePosition pos) { + return new BlockPos(pos.x(), pos.y(), pos.z()); + } private final Logger logger = Logger.getLogger(getClass().getCanonicalName()); + private final NativeAdapter nativeAdapter = new NativeAdapter() { + @Override + public NativeBlockState toNative(BlockState state) { + return new PaperweightNativeBlockState(adapt(state)); + } + + @Override + public BlockState fromNative(NativeBlockState state) { + return adapt(((PaperweightNativeBlockState) state).delegate()); + } + + @Override public NativePosition newBlockPos(BlockVector3 pos) { + return pos; + } + }; + private final Field serverWorldsField; private final Method getChunkFutureMethod; private final Field chunkProviderExecutorField; @@ -259,6 +282,10 @@ public PaperweightAdapter() throws NoSuchFieldException, NoSuchMethodException { } } + public NativeAdapter asNativeAdapter() { + return nativeAdapter; + } + @Override public DataFixer getDataFixer() { return this.dataFixer; @@ -417,8 +444,8 @@ public void setBiome(Location location, BiomeType biome) { } @Override - public WorldNativeAccess createWorldNativeAccess(World world) { - return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle())); + public NativeWorld createNativeInterface(World world) { + return new PaperweightNativeWorld(this, ((CraftWorld) world).getHandle()); } private static net.minecraft.core.Direction adapt(Direction face) { @@ -1090,7 +1117,7 @@ private LinListTag toNativeList(net.minecraft.nbt.ListTag foreign) throws Sec * @param foreign structure to convert * @return non-native structure */ - Tag fromNative(LinTag foreign) { + public Tag fromNative(LinTag foreign) { if (foreign == null) { return null; } diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightWorldNativeAccess.java deleted file mode 100644 index ea0a4ab8dc..0000000000 --- a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightWorldNativeAccess.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.bukkit.adapter.impl.v1_21_4; - -import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.internal.block.BlockStateIdAccess; -import com.sk89q.worldedit.internal.wna.WorldNativeAccess; -import com.sk89q.worldedit.util.SideEffect; -import com.sk89q.worldedit.util.SideEffectSet; -import com.sk89q.worldedit.world.block.BlockState; -import net.minecraft.core.BlockPos; -import net.minecraft.nbt.Tag; -import net.minecraft.server.level.FullChunkStatus; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.block.Block; -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.craftbukkit.CraftWorld; -import org.bukkit.craftbukkit.block.data.CraftBlockData; -import org.bukkit.event.block.BlockPhysicsEvent; -import org.enginehub.linbus.tree.LinCompoundTag; - -import java.lang.ref.WeakReference; -import java.util.Objects; -import javax.annotation.Nullable; - -public class PaperweightWorldNativeAccess implements WorldNativeAccess { - private static final int UPDATE = 1; - private static final int NOTIFY = 2; - - private final PaperweightAdapter adapter; - private final WeakReference world; - private SideEffectSet sideEffectSet; - - public PaperweightWorldNativeAccess(PaperweightAdapter adapter, WeakReference world) { - this.adapter = adapter; - this.world = world; - } - - private ServerLevel getWorld() { - return Objects.requireNonNull(world.get(), "The reference to the world was lost"); - } - - @Override - public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { - this.sideEffectSet = sideEffectSet; - } - - @Override - public LevelChunk getChunk(int x, int z) { - return getWorld().getChunk(x, z); - } - - @Override - public net.minecraft.world.level.block.state.BlockState toNative(BlockState state) { - int stateId = BlockStateIdAccess.getBlockStateId(state); - return BlockStateIdAccess.isValidInternalId(stateId) - ? Block.stateById(stateId) - : ((CraftBlockData) BukkitAdapter.adapt(state)).getState(); - } - - @Override - public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk chunk, BlockPos position) { - return chunk.getBlockState(position); - } - - @Nullable - @Override - public net.minecraft.world.level.block.state.BlockState setBlockState(LevelChunk chunk, BlockPos position, net.minecraft.world.level.block.state.BlockState state) { - return chunk.setBlockState(position, state, false, this.sideEffectSet.shouldApply(SideEffect.UPDATE)); - } - - @Override - public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(net.minecraft.world.level.block.state.BlockState block, BlockPos position) { - return Block.updateFromNeighbourShapes(block, getWorld(), position); - } - - @Override - public BlockPos getPosition(int x, int y, int z) { - return new BlockPos(x, y, z); - } - - @Override - public void updateLightingForBlock(BlockPos position) { - getWorld().getChunkSource().getLightEngine().checkBlock(position); - } - - @Override - public boolean updateTileEntity(BlockPos position, LinCompoundTag tag) { - // We will assume that the tile entity was created for us - BlockEntity tileEntity = getWorld().getBlockEntity(position); - if (tileEntity == null) { - return false; - } - Tag nativeTag = adapter.fromNative(tag); - PaperweightAdapter.readTagIntoTileEntity((net.minecraft.nbt.CompoundTag) nativeTag, tileEntity); - return true; - } - - @Override - public void notifyBlockUpdate(LevelChunk chunk, BlockPos position, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { - if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { - getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY); - } - } - - @Override - public boolean isChunkTicking(LevelChunk chunk) { - return chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING); - } - - @Override - public void markBlockChanged(LevelChunk chunk, BlockPos position) { - if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { - getWorld().getChunkSource().blockChanged(position); - } - } - - @Override - public void notifyNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { - ServerLevel world = getWorld(); - if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { - world.updateNeighborsAt(pos, oldState.getBlock()); - } else { - // When we don't want events, manually run the physics without them. - Block block = oldState.getBlock(); - fireNeighborChanged(pos, world, block, pos.west()); - fireNeighborChanged(pos, world, block, pos.east()); - fireNeighborChanged(pos, world, block, pos.below()); - fireNeighborChanged(pos, world, block, pos.above()); - fireNeighborChanged(pos, world, block, pos.north()); - fireNeighborChanged(pos, world, block, pos.south()); - } - if (newState.hasAnalogOutputSignal()) { - world.updateNeighbourForOutputSignal(pos, newState.getBlock()); - } - } - - @Override - public void updateBlock(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { - ServerLevel world = getWorld(); - newState.onPlace(world, pos, oldState, false); - } - - private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) { - world.getBlockState(neighborPos).handleNeighborChanged(world, neighborPos, block, ExperimentalRedstoneUtils.initialOrientation(world, null, null), false); - } - - @Override - public void updateNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState, int recursionLimit) { - ServerLevel world = getWorld(); - oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); - if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { - CraftWorld craftWorld = world.getWorld(); - BlockPhysicsEvent event = new BlockPhysicsEvent(craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), CraftBlockData.fromData(newState)); - world.getCraftServer().getPluginManager().callEvent(event); - if (event.isCancelled()) { - return; - } - } - newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit); - newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); - } - - @Override - public void onBlockStateChange(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { - getWorld().onBlockStateChange(pos, oldState, newState); - } -} diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/StaticRefraction.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/StaticRefraction.java index 9e5272571e..ee88c7c83b 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/StaticRefraction.java +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/StaticRefraction.java @@ -88,4 +88,37 @@ public final class StaticRefraction { public static final String DESTROY_BLOCK_BREAKING_ENTITY_MAX_UPDATE = Refraction.pickName( "destroyBlock", "a" ); + public static final String GET_VISIBLE_CHUNK_IF_PRESENT = Refraction.pickName( + "getVisibleChunkIfPresent", "b" + ); + public static final String CHANGED_BLOCKS_PER_SECTION = Refraction.pickName( + "changedBlocksPerSection", "n" + ); + public static final String HAS_CHANGED_SECTIONS = Refraction.pickName( + "hasChangedSections", "m" + ); + /** + * {@code updateBlockEntityTicker(BlockEntity blockEntity)}. + */ + public static final String UPDATE_BLOCK_ENTITY_TICKER = Refraction.pickName( + "updateBlockEntityTicker", "c" + ); + public static final String PALETTED_CONTAINER_DATA = Refraction.pickName( + "data", "d" + ); + public static final String PALETTED_CONTAINER_DATA_CLASS = Refraction.pickName( + "net.minecraft.world.level.chunk.PalettedContainer$Data", "net.minecraft.world.level.chunk.DataPaletteBlock$c" + ); + public static final String PALETTED_CONTAINER_DATA_STORAGE = Refraction.pickName( + "storage", "b" + ); + public static final String PALETTED_CONTAINER_DATA_PALETTE = Refraction.pickName( + "palette", "c" + ); + public static final String CREATE_OR_REUSE_DATA = Refraction.pickName( + "createOrReuseData", "a" + ); + public static final String COPY_FROM = Refraction.pickName( + "copyFrom", "a" + ); } diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeBlockState.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeBlockState.java new file mode 100644 index 0000000000..973fc9f93b --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeBlockState.java @@ -0,0 +1,51 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.impl.v1_21_4.wna; + +import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_4.PaperweightAdapter; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +public record PaperweightNativeBlockState(BlockState delegate) implements NativeBlockState { + @Override + public boolean isSame(NativeBlockState other) { + return this.delegate == ((PaperweightNativeBlockState) other).delegate; + } + + @Override + public boolean isSameBlockType(NativeBlockState other) { + return delegate.getBlock() == ((PaperweightNativeBlockState) other).delegate.getBlock(); + } + + @Override + public boolean hasBlockEntity() { + return delegate.hasBlockEntity(); + } + + @Override + public NativeBlockState updateFromNeighbourShapes(NativeWorld world, NativePosition position) { + return new PaperweightNativeBlockState(Block.updateFromNeighbourShapes( + delegate, ((PaperweightNativeWorld) world).delegate(), PaperweightAdapter.adaptPos(position) + )); + } +} diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunk.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunk.java new file mode 100644 index 0000000000..afa86c4bb5 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunk.java @@ -0,0 +1,219 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.impl.v1_21_4.wna; + +import com.google.common.base.Preconditions; +import com.google.common.base.Throwables; +import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_4.PaperweightAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_4.StaticRefraction; +import com.sk89q.worldedit.internal.util.collection.ChunkSectionMask; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativeChunk; +import com.sk89q.worldedit.internal.wna.NativeChunkSection; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import com.sk89q.worldedit.internal.wna.WNASharedImpl; +import com.sk89q.worldedit.math.BlockVector3; +import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet; +import it.unimi.dsi.fastutil.shorts.ShortSet; +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.FullChunkStatus; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.levelgen.Heightmap; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.EnumSet; +import java.util.Set; +import javax.annotation.Nullable; + +public record PaperweightNativeChunk(NativeWorld owner, LevelChunk delegate) implements NativeChunk { + private static final MethodHandle GET_VISIBLE_CHUNK_IF_PRESENT; + private static final MethodHandle GET_CHANGED_BLOCKS_PER_SECTION; + private static final MethodHandle SET_HAS_CHANGED_SECTIONS; + private static final MethodHandle UPDATE_BLOCK_ENTITY_TICKER; + + static { + try { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + Method m = ServerChunkCache.class.getDeclaredMethod( + StaticRefraction.GET_VISIBLE_CHUNK_IF_PRESENT, long.class + ); + m.setAccessible(true); + GET_VISIBLE_CHUNK_IF_PRESENT = lookup.unreflect(m); + + Field f = ChunkHolder.class.getDeclaredField(StaticRefraction.CHANGED_BLOCKS_PER_SECTION); + f.setAccessible(true); + GET_CHANGED_BLOCKS_PER_SECTION = lookup.unreflectGetter(f); + + f = ChunkHolder.class.getDeclaredField(StaticRefraction.HAS_CHANGED_SECTIONS); + f.setAccessible(true); + SET_HAS_CHANGED_SECTIONS = lookup.unreflectSetter(f); + + m = LevelChunk.class.getDeclaredMethod(StaticRefraction.UPDATE_BLOCK_ENTITY_TICKER, BlockEntity.class); + m.setAccessible(true); + UPDATE_BLOCK_ENTITY_TICKER = lookup.unreflect(m); + } catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException e) { + throw new AssertionError(e); + } + } + + private static final Set HEIGHTMAPS = EnumSet.of( + Heightmap.Types.WORLD_SURFACE, + Heightmap.Types.OCEAN_FLOOR, + Heightmap.Types.MOTION_BLOCKING, + Heightmap.Types.MOTION_BLOCKING_NO_LEAVES + ); + + @Override + public NativeWorld getWorld() { + return owner; + } + + @Override + public boolean isTicking() { + return delegate.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING); + } + + @Override + public NativePosition getWorldPos(int offsetX, int offsetY, int offsetZ) { + ChunkPos pos = delegate.getPos(); + return new BlockVector3(pos.getBlockX(offsetX), offsetY, pos.getBlockZ(offsetZ)); + } + + @Override + public NativeBlockState getBlockState(NativePosition blockPos) { + return new PaperweightNativeBlockState(delegate.getBlockState(PaperweightAdapter.adaptPos(blockPos))); + } + + @Override + public @Nullable NativeBlockState setBlockState(NativePosition blockPos, NativeBlockState newState, boolean update) { + return new PaperweightNativeBlockState(delegate.setBlockState( + PaperweightAdapter.adaptPos(blockPos), + ((PaperweightNativeBlockState) newState).delegate(), + false, + update + )); + } + + @Override + public void markSectionChanged(int index, ChunkSectionMask changed) { + ServerChunkCache serverChunkCache = (ServerChunkCache) delegate.getLevel().getChunkSource(); + ChunkHolder holder; + try { + holder = (ChunkHolder) GET_VISIBLE_CHUNK_IF_PRESENT.invoke( + serverChunkCache, + delegate.getPos().toLong() + ); + } catch (Throwable e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + if (holder != null) { + ShortSet[] changedBlocksPerSection; + try { + changedBlocksPerSection = (ShortSet[]) GET_CHANGED_BLOCKS_PER_SECTION.invoke(holder); + } catch (Throwable e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + if (changedBlocksPerSection[index] == null) { + try { + SET_HAS_CHANGED_SECTIONS.invoke(holder, true); + } catch (Throwable e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + changedBlocksPerSection[index] = new ShortOpenHashSet(changed.asShortCollection()); + } else { + changedBlocksPerSection[index].addAll(changed.asShortCollection()); + } + // Trick to get the holder into the broadcast set + serverChunkCache.onChunkReadyToSend(holder); + } + } + + @Override + public void updateHeightmaps() { + Heightmap.primeHeightmaps(delegate, HEIGHTMAPS); + } + + @Override + public void updateLightingForSectionAirChange(int index, boolean onlyAir) { + delegate.getLevel().getLightEngine().updateSectionStatus( + SectionPos.of(delegate.getPos(), delegate.getLevel().getSectionYFromSectionIndex(index)), + onlyAir + ); + } + + @Override + public void removeSectionBlockEntity(int chunkX, int chunkY, int chunkZ) { + delegate.removeBlockEntity(delegate.getPos().getBlockAt(chunkX, chunkY, chunkZ)); + } + + @SuppressWarnings("deprecation") + @Override + public void initializeBlockEntity(int chunkX, int chunkY, int chunkZ, NativeBlockState newState) { + BlockPos pos = delegate.getPos().getBlockAt(chunkX, chunkY, chunkZ); + BlockEntity blockEntity = delegate.getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK); + BlockState nativeState = ((PaperweightNativeBlockState) newState).delegate(); + if (blockEntity == null) { + blockEntity = ((EntityBlock) nativeState.getBlock()).newBlockEntity(pos, nativeState); + if (blockEntity != null) { + delegate.addAndRegisterBlockEntity(blockEntity); + } + } else { + blockEntity.setBlockState(nativeState); + try { + UPDATE_BLOCK_ENTITY_TICKER.invoke(blockEntity); + } catch (Throwable e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + } + } + + @Override + public NativeChunkSection getChunkSection(int index) { + return new PaperweightNativeChunkSection(delegate.getSection(index)); + } + + @Override + public NativeChunkSection setChunkSection(int index, NativeChunkSection section, ChunkSectionMask modifiedBlocks) { + Preconditions.checkPositionIndex(index, delegate.getSectionsCount()); + LevelChunkSection[] chunkSections = delegate.getSections(); + var oldSection = new PaperweightNativeChunkSection(chunkSections[index]); + chunkSections[index] = ((PaperweightNativeChunkSection) section).delegate(); + WNASharedImpl.postChunkSectionReplacement(this, index, oldSection, section, modifiedBlocks); + delegate.markUnsaved(); + return oldSection; + } +} diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunkSection.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunkSection.java new file mode 100644 index 0000000000..c5ff3b019d --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunkSection.java @@ -0,0 +1,53 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.impl.v1_21_4.wna; + +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativeChunkSection; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunkSection; + +public record PaperweightNativeChunkSection(LevelChunkSection delegate) implements NativeChunkSection { + @Override + public boolean isOnlyAir() { + return delegate.hasOnlyAir(); + } + + @Override + public NativeBlockState getThenSetBlock(int i, int j, int k, NativeBlockState blockState) { + BlockState nativeState = ((PaperweightNativeBlockState) blockState).delegate(); + if (isOnlyAir() && nativeState.isAir()) { + return blockState; + } + return new PaperweightNativeBlockState(delegate.setBlockState(i, j, k, nativeState, false)); + } + + @Override + public NativeBlockState getBlock(int i, int j, int k) { + return new PaperweightNativeBlockState(delegate.getBlockState(i, j, k)); + } + + @Override + public NativeChunkSection copy() { + return new PaperweightNativeChunkSection(new LevelChunkSection( + delegate.getStates().copy(), delegate.getBiomes().copy() + )); + } +} diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeWorld.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeWorld.java new file mode 100644 index 0000000000..de32028e24 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeWorld.java @@ -0,0 +1,167 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.impl.v1_21_4.wna; + +import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_4.PaperweightAdapter; +import com.sk89q.worldedit.internal.wna.NativeAdapter; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativeChunk; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.profiling.Profiler; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.event.block.BlockPhysicsEvent; +import org.enginehub.linbus.tree.LinCompoundTag; + +public record PaperweightNativeWorld(PaperweightAdapter adapter, ServerLevel delegate) implements NativeWorld { + @Override + public NativeAdapter getAdapter() { + return adapter.asNativeAdapter(); + } + + @Override + public int getSectionIndex(int y) { + return delegate.getSectionIndex(y); + } + + @Override + public int getYForSectionIndex(int index) { + return SectionPos.sectionToBlockCoord(delegate.getSectionYFromSectionIndex(index)); + } + + @Override + public NativeChunk getChunk(int chunkX, int chunkZ) { + return new PaperweightNativeChunk(this, delegate.getChunk(chunkX, chunkZ)); + } + + @Override + public void notifyBlockUpdate(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { + delegate.sendBlockUpdated( + PaperweightAdapter.adaptPos(pos), + ((PaperweightNativeBlockState) oldState).delegate(), + ((PaperweightNativeBlockState) newState).delegate(), + Block.UPDATE_NEIGHBORS | Block.UPDATE_CLIENTS + ); + } + + @Override + public void markBlockChanged(NativePosition pos) { + delegate.getChunkSource().blockChanged(PaperweightAdapter.adaptPos(pos)); + } + + @Override + public void updateLightingForBlock(NativePosition position) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("updateSkyLightSources"); + /* Paper removes the update here */ + profilerFiller.popPush("queueCheckLight"); + delegate.getChunkSource().getLightEngine().checkBlock(PaperweightAdapter.adaptPos(position)); + profilerFiller.pop(); + } + + @Override + public boolean updateTileEntity(NativePosition position, LinCompoundTag tag) { + CompoundTag nativeTag = (CompoundTag) adapter.fromNative(tag); + BlockPos nativePos = PaperweightAdapter.adaptPos(position); + BlockEntity tileEntity = delegate.getChunkAt(nativePos).getBlockEntity(nativePos); + if (tileEntity == null) { + return false; + } + tileEntity.loadWithComponents(nativeTag, delegate.registryAccess()); + tileEntity.setChanged(); + return true; + } + + @Override + public void notifyNeighbors( + NativePosition pos, NativeBlockState oldState, NativeBlockState newState, boolean events + ) { + BlockPos nativePos = PaperweightAdapter.adaptPos(pos); + if (events) { + delegate.updateNeighborsAt(nativePos, ((PaperweightNativeBlockState) oldState).delegate().getBlock()); + } else { + // When we don't want events, manually run the physics without them. + Block block = ((PaperweightNativeBlockState) oldState).delegate().getBlock(); + fireNeighborChanged(nativePos, delegate, block, nativePos.west()); + fireNeighborChanged(nativePos, delegate, block, nativePos.east()); + fireNeighborChanged(nativePos, delegate, block, nativePos.below()); + fireNeighborChanged(nativePos, delegate, block, nativePos.above()); + fireNeighborChanged(nativePos, delegate, block, nativePos.north()); + fireNeighborChanged(nativePos, delegate, block, nativePos.south()); + } + BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate(); + if (nativeNewState.hasAnalogOutputSignal()) { + delegate.updateNeighbourForOutputSignal(nativePos, nativeNewState.getBlock()); + } + } + + private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) { + world.getBlockState(neighborPos).handleNeighborChanged(world, neighborPos, block, null, false); + } + + @Override + public void updateBlock(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { + BlockPos nativePos = PaperweightAdapter.adaptPos(pos); + BlockState nativeOldState = ((PaperweightNativeBlockState) oldState).delegate(); + BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate(); + nativeOldState.onRemove(delegate, nativePos, nativeNewState, false); + nativeNewState.onPlace(delegate, nativePos, nativeOldState, false); + } + + @Override + public void updateNeighbors( + NativePosition pos, NativeBlockState oldState, NativeBlockState newState, int recursionLimit, boolean events + ) { + BlockPos nativePos = PaperweightAdapter.adaptPos(pos); + BlockState nativeOldState = ((PaperweightNativeBlockState) oldState).delegate(); + BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate(); + nativeOldState.updateIndirectNeighbourShapes(delegate, nativePos, Block.UPDATE_CLIENTS, recursionLimit); + if (events) { + BlockPhysicsEvent event = new BlockPhysicsEvent( + CraftBlock.at(delegate, nativePos), + CraftBlockData.fromData(nativeNewState) + ); + delegate.getCraftServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + } + nativeNewState.updateNeighbourShapes(delegate, nativePos, Block.UPDATE_CLIENTS, recursionLimit); + nativeNewState.updateIndirectNeighbourShapes(delegate, nativePos, Block.UPDATE_CLIENTS, recursionLimit); + } + + @Override + public void onBlockStateChange(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { + delegate.onBlockStateChange( + PaperweightAdapter.adaptPos(pos), + ((PaperweightNativeBlockState) oldState).delegate(), + ((PaperweightNativeBlockState) newState).delegate() + ); + } +} diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index af2d4c00bf..93600472ba 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -83,14 +83,12 @@ tasks.named("shadowJar") { include(dependency("org.antlr:antlr4-runtime")) include(dependency("org.bstats:")) include(dependency("io.papermc:paperlib")) - include(dependency("it.unimi.dsi:fastutil")) include(dependency("com.sk89q.lib:jlibnoise")) exclude(dependency("$group:$name")) relocate("org.bstats", "com.sk89q.worldedit.bstats") relocate("io.papermc.lib", "com.sk89q.worldedit.bukkit.paperlib") - relocate("it.unimi.dsi.fastutil", "com.sk89q.worldedit.bukkit.fastutil") relocate("net.royawesome.jlibnoise", "com.sk89q.worldedit.jlibnoise") } project.project(":worldedit-bukkit:adapters").subprojects.forEach { 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 6f94aa6886..e3e9282d43 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 @@ -32,7 +32,8 @@ import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.internal.util.LogManagerCompat; -import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import com.sk89q.worldedit.internal.wna.WNASharedImpl; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; @@ -91,7 +92,7 @@ public class BukkitWorld extends AbstractWorld { } private final WeakReference worldRef; - private final WorldNativeAccess worldNativeAccess; + private final NativeWorld nativeWorld; /** * Construct the object. @@ -102,12 +103,17 @@ public BukkitWorld(World world) { this.worldRef = new WeakReference<>(world); BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); if (adapter != null) { - this.worldNativeAccess = adapter.createWorldNativeAccess(world); + this.nativeWorld = adapter.createNativeInterface(world); } else { - this.worldNativeAccess = null; + this.nativeWorld = null; } } + @Override + public @Nullable NativeWorld getNativeInterface() { + return nativeWorld; + } + @Override public List getEntities(Region region) { World world = getWorld(); @@ -480,9 +486,9 @@ public com.sk89q.worldedit.world.block.BlockState getBlock(BlockVector3 position @Override public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) { clearContainerBlockContents(position); - if (worldNativeAccess != null) { + if (nativeWorld != null) { try { - return worldNativeAccess.setBlock(position, block, sideEffects); + return WNASharedImpl.setBlock(nativeWorld, position, block, sideEffects); } catch (Exception e) { if (block instanceof BaseBlock baseBlock && baseBlock.getNbt() != null) { LOGGER.warn("Tried to set a corrupt tile entity at " + position.toString() @@ -514,8 +520,8 @@ public BaseBlock getFullBlock(BlockVector3 position) { @Override public Set applySideEffects(BlockVector3 position, com.sk89q.worldedit.world.block.BlockState previousType, SideEffectSet sideEffectSet) { - if (worldNativeAccess != null) { - worldNativeAccess.applySideEffects(position, previousType, sideEffectSet); + if (nativeWorld != null) { + WNASharedImpl.applySideEffects(nativeWorld, sideEffectSet, position, previousType); return Sets.intersection( WorldEditPlugin.getInstance().getInternalPlatform().getSupportedSideEffects(), sideEffectSet.getSideEffectsToApply() diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java index ee10cb7669..770b407bf0 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java @@ -24,7 +24,7 @@ import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.internal.wna.NativeWorld; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; @@ -100,12 +100,12 @@ default void tickWatchdog() { BaseBlock getFullBlock(Location location); /** - * Create a {@link WorldNativeAccess} for the given world reference. + * Create a {@link NativeWorld} for the given world reference. * * @param world the world reference * @return the native access object */ - WorldNativeAccess createWorldNativeAccess(World world); + NativeWorld createNativeInterface(World world); /** * Get the state for the given entity. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 5d98f6554a..4ba391d11c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -44,6 +44,7 @@ import com.sk89q.worldedit.extent.world.SideEffectExtent; import com.sk89q.worldedit.extent.world.SurvivalModeExtent; import com.sk89q.worldedit.extent.world.WatchdogTickingExtent; +import com.sk89q.worldedit.extent.world.internal.SectionBufferingExtent; import com.sk89q.worldedit.function.GroundFunction; import com.sk89q.worldedit.function.RegionMaskingFilter; import com.sk89q.worldedit.function.biome.BiomeReplace; @@ -86,6 +87,7 @@ import com.sk89q.worldedit.internal.expression.ExpressionTimeoutException; import com.sk89q.worldedit.internal.expression.LocalSlot.Variable; import com.sk89q.worldedit.internal.util.LogManagerCompat; +import com.sk89q.worldedit.internal.wna.NativeWorld; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.MathUtils; import com.sk89q.worldedit.math.Vector2; @@ -115,6 +117,7 @@ import com.sk89q.worldedit.util.eventbus.EventBus; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; +import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.NullWorld; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; @@ -254,7 +257,16 @@ public String getDisplayName() { Extent extent; // These extents are ALWAYS used - extent = traceIfNeeded(sideEffectExtent = new SideEffectExtent(world)); + sideEffectExtent = new SideEffectExtent(world); + NativeWorld nativeInterface = null; + boolean usingNativeInterface = WorldEdit.getInstance().getConfiguration().chunkSectionEditing + && world instanceof AbstractWorld internalWorld + && (nativeInterface = internalWorld.getNativeInterface()) != null; + if (usingNativeInterface) { + extent = traceIfNeeded(new SectionBufferingExtent(nativeInterface, sideEffectExtent)); + } else { + extent = traceIfNeeded(sideEffectExtent); + } if (watchdog != null) { // Reset watchdog before world placement WatchdogTickingExtent watchdogExtent = new WatchdogTickingExtent(extent, watchdog); @@ -269,7 +281,11 @@ public String getDisplayName() { this.bypassReorderHistory = traceIfNeeded(new DataValidatorExtent(extent, world)); // This extent can be skipped by calling rawSetBlock() - extent = traceIfNeeded(batchingExtent = new BatchingExtent(extent)); + if (!usingNativeInterface) { + // We need to ensure that blocks are not immediately committed to the world for masks + // This is done by the SectionBufferingExtent normally, but if we can't use it we need a fallback + extent = traceIfNeeded(batchingExtent = new BatchingExtent(extent)); + } @SuppressWarnings("deprecation") MultiStageReorder reorder = new MultiStageReorder(extent, false); extent = traceIfNeeded(reorderExtent = reorder); @@ -619,12 +635,13 @@ public void setBatchingChunks(boolean batchingChunks) { } return; } - assert batchingExtent != null : "same nullness as chunkBatchingExtent"; if (!batchingChunks && isBatchingChunks()) { internalFlushSession(); } chunkBatchingExtent.setEnabled(batchingChunks); - batchingExtent.setEnabled(!batchingChunks); + if (batchingExtent != null) { + batchingExtent.setEnabled(!batchingChunks); + } } /** @@ -653,8 +670,9 @@ public void disableBuffering() { setReorderMode(ReorderMode.NONE); if (chunkBatchingExtent != null) { chunkBatchingExtent.setEnabled(false); - assert batchingExtent != null : "same nullness as chunkBatchingExtent"; - batchingExtent.setEnabled(true); + if (batchingExtent != null) { + batchingExtent.setEnabled(true); + } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalConfiguration.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalConfiguration.java index c3a2748451..8296f3eeeb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalConfiguration.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalConfiguration.java @@ -94,6 +94,7 @@ public abstract class LocalConfiguration { public boolean commandBlockSupport = false; public String defaultLocaleName = "default"; public Locale defaultLocale = Locale.getDefault(); + public boolean chunkSectionEditing = true; @SuppressWarnings("deprecation") protected String[] getDefaultDisallowedBlocks() { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/internal/SectionBufferingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/internal/SectionBufferingExtent.java new file mode 100644 index 0000000000..0e794c7b96 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/internal/SectionBufferingExtent.java @@ -0,0 +1,280 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.world.internal; + +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.Watchdog; +import com.sk89q.worldedit.extent.AbstractBufferingExtent; +import com.sk89q.worldedit.extent.world.SideEffectExtent; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.function.operation.RunContext; +import com.sk89q.worldedit.internal.util.collection.ChunkSectionMask; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativeChunk; +import com.sk89q.worldedit.internal.wna.NativeChunkSection; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import com.sk89q.worldedit.internal.wna.WNASharedImpl; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.util.collection.BlockMap; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import org.enginehub.linbus.tree.LinCompoundTag; + +import java.util.Map; +import javax.annotation.Nullable; + +/** + * A special extent that buffers all changes as {@link NativeChunkSection chunk sections}. This allows highly optimized + * block setting, but has a higher likelihood of different semantics compared to traditional block setting. + */ +public class SectionBufferingExtent extends AbstractBufferingExtent { + + private static long getTopSectionKey(BlockVector3 sectionPos) { + return ((long) sectionPos.x() & 0xFF_FF_FF_FFL) | (((long) sectionPos.z() & 0xFF_FF_FF_FFL) << 32); + } + + private record SectionData( + NativeChunkSection section, + ChunkSectionMask modified + ) { + } + + private final Long2ObjectMap> sectionTable = new Long2ObjectLinkedOpenHashMap<>(); + private final BlockMap blockEntityMap = BlockMap.create(); + private final NativeWorld nativeWorld; + private final SideEffectExtent sideEffectExtent; + private boolean enabled; + + public SectionBufferingExtent(NativeWorld nativeWorld, SideEffectExtent sideEffectExtent) { + this(nativeWorld, sideEffectExtent, true); + } + + public SectionBufferingExtent(NativeWorld nativeWorld, SideEffectExtent sideEffectExtent, boolean enabled) { + super(sideEffectExtent); + this.nativeWorld = nativeWorld; + this.sideEffectExtent = sideEffectExtent; + this.enabled = enabled; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean commitRequired() { + return enabled; + } + + private BlockVector3 toSectionPos(BlockVector3 position) { + return BlockVector3.at( + position.x() >> 4, + nativeWorld.getSectionIndex(position.y()), + position.z() >> 4 + ); + } + + private @Nullable SectionData getSectionData(BlockVector3 sectionPos) { + Int2ObjectArrayMap sections = sectionTable.get(getTopSectionKey(sectionPos)); + if (sections == null) { + return null; + } + return sections.get(sectionPos.y()); + } + + @Override + public > boolean setBlock(BlockVector3 location, B block) throws WorldEditException { + if (!enabled) { + return setDelegateBlock(location, block); + } + if (!sideEffectExtent.isPostEditSimulationEnabled()) { + throw new IllegalStateException("SectionBufferingExtent requires SideEffectExtent to have post-edit simulation enabled"); + } + BlockVector3 sectionPos = toSectionPos(location); + SectionData data = getSectionData(sectionPos); + if (data != null) { + NativeBlockState newState = nativeWorld.getAdapter().toNative(block.toImmutableState()); + NativeBlockState oldState = data.section.getThenSetBlock( + location.x() & 0xF, location.y() & 0xF, location.z() & 0xF, + newState + ); + if (oldState == newState && block.toBaseBlock().getNbt() == null) { + // Optimize out the change if it's the same + return false; + } + } else { + if (block.toBaseBlock().getNbt() == null) { + BaseBlock existingState = getExtent().getFullBlock(location); + if (existingState.toImmutableState() == block && existingState.getNbt() == null) { + // Optimize out the change if it's the same + return false; + } + } + data = new SectionData( + nativeWorld.getChunk(sectionPos.x(), sectionPos.z()).getChunkSection(sectionPos.y()).copy(), + new ChunkSectionMask() + ); + sectionTable.computeIfAbsent(getTopSectionKey(sectionPos), k -> new Int2ObjectArrayMap<>()) + .put(sectionPos.y(), data); + data.section.getThenSetBlock( + location.x() & 0xF, location.y() & 0xF, location.z() & 0xF, + nativeWorld.getAdapter().toNative(block.toImmutableState()) + ); + } + + LinCompoundTag nbt = block.toBaseBlock().getNbt(); + if (nbt != null) { + blockEntityMap.put(location, nbt); + } + + data.modified.set(location.x() & 0xF, location.y() & 0xF, location.z() & 0xF); + + return true; + } + + @Override + protected BaseBlock getBufferedFullBlock(BlockVector3 position) { + if (!enabled) { + // Early exit if we're not enabled. + return null; + } + BlockVector3 sectionPos = toSectionPos(position); + SectionData data = getSectionData(sectionPos); + if (data == null) { + return null; + } + NativeBlockState state = data.section.getBlock(position.x() & 0xF, position.y() & 0xF, position.z() & 0xF); + return nativeWorld.getAdapter().fromNative(state).toBaseBlock(blockEntityMap.get(position)); + } + + @Override + protected Operation commitBefore() { + if (!commitRequired()) { + return null; + } + return new Operation() { + @Override + public Operation resume(RunContext run) { + finishOperation(); + return null; + } + + @Override + public void cancel() { + } + }; + } + + private void finishOperation() { + record SectionDataWithOld( + NativeChunkSection section, + NativeChunkSection oldSection, + ChunkSectionMask modified + ) { + } + + + Watchdog watchdog = WorldEdit.getInstance().getPlatformManager() + .queryCapability(Capability.GAME_HOOKS).getWatchdog(); + + Long2ObjectMap> effectData = new Long2ObjectLinkedOpenHashMap<>(); + + int ops = 0; + for (Long2ObjectMap.Entry> entry : sectionTable.long2ObjectEntrySet()) { + int chunkX = (int) entry.getLongKey(); + int chunkZ = (int) (entry.getLongKey() >> 32); + NativeChunk chunk = nativeWorld.getChunk(chunkX, chunkZ); + Int2ObjectArrayMap oldSections = new Int2ObjectArrayMap<>(entry.getValue().size()); + for (Int2ObjectArrayMap.Entry sectionEntry : entry.getValue().int2ObjectEntrySet()) { + SectionData data = sectionEntry.getValue(); + NativeChunkSection old = chunk.setChunkSection(sectionEntry.getIntKey(), data.section, data.modified); + oldSections.put(sectionEntry.getIntKey(), new SectionDataWithOld(data.section, old, data.modified)); + } + effectData.put(entry.getLongKey(), oldSections); + + chunk.updateHeightmaps(); + + if (watchdog != null) { + ops = updateWatchdog(ops, watchdog); + } + } + sectionTable.clear(); + + for (Map.Entry entry : blockEntityMap.entrySet()) { + BlockVector3 position = entry.getKey(); + LinCompoundTag tag = entry.getValue(); + NativePosition pos = nativeWorld.getAdapter().newBlockPos(position); + WNASharedImpl.updateTileEntity(nativeWorld, tag, pos); + + if (watchdog != null) { + ops = updateWatchdog(ops, watchdog); + } + } + blockEntityMap.clear(); + + SideEffectSet sideEffectSet = sideEffectExtent.getSideEffectSet(); + SideEffectSet perBlockEffects = sideEffectSet + .with(SideEffect.NETWORK, SideEffect.State.OFF); + for (Long2ObjectMap.Entry> entry : effectData.long2ObjectEntrySet()) { + int chunkX = (int) entry.getLongKey(); + int chunkZ = (int) (entry.getLongKey() >> 32); + NativeChunk chunk = nativeWorld.getChunk(chunkX, chunkZ); + for (Int2ObjectArrayMap.Entry sectionEntry : entry.getValue().int2ObjectEntrySet()) { + int index = sectionEntry.getIntKey(); + SectionDataWithOld data = sectionEntry.getValue(); + if (sideEffectSet.shouldApply(SideEffect.NETWORK)) { + chunk.markSectionChanged(index, data.modified); + } + int sectionBlockY = nativeWorld.getYForSectionIndex(index); + data.modified.forEach((x, y, z) -> { + NativePosition pos = chunk.getWorldPos(x, sectionBlockY + y, z); + WNASharedImpl.applySideEffectsNoLookups( + nativeWorld, chunk, perBlockEffects, pos, + data.oldSection.getBlock(x, y, z), data.section.getBlock(x, y, z) + ); + }); + } + + if (watchdog != null) { + ops = updateWatchdog(ops, watchdog); + } + } + } + + private static int updateWatchdog(int ops, Watchdog watchdog) { + ops++; + if (ops == 100) { + watchdog.tick(); + ops = 0; + } + return ops; + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/collection/ChunkSectionMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/collection/ChunkSectionMask.java new file mode 100644 index 0000000000..2857fcf5d4 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/collection/ChunkSectionMask.java @@ -0,0 +1,107 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.util.collection; + +import it.unimi.dsi.fastutil.shorts.AbstractShortCollection; +import it.unimi.dsi.fastutil.shorts.ShortCollection; +import it.unimi.dsi.fastutil.shorts.ShortIterator; + +import java.util.BitSet; + +/** + * A mask for a chunk section. + */ +public class ChunkSectionMask { + public static int index(int x, int y, int z) { + // Each value is 0-15, so 4 bits + // NOTE: This order specifically matches the order used by SectionPos in Minecraft, do not change unless they do + return (x << 8) | (z << 4) | y; + } + + @FunctionalInterface + public interface PosConsumer { + void apply(int x, int y, int z); + } + + private final BitSet mask = new BitSet(4096); + + public boolean isSet(int x, int y, int z) { + return mask.get(index(x, y, z)); + } + + public void set(int x, int y, int z) { + mask.set(index(x, y, z)); + } + + public void clear(int x, int y, int z) { + mask.clear(index(x, y, z)); + } + + public void clear() { + mask.clear(); + } + + public void setAll() { + mask.set(0, 4096); + } + + public void forEach(PosConsumer consumer) { + for (int i = mask.nextSetBit(0); i >= 0; i = mask.nextSetBit(i + 1)) { + consumer.apply((i >> 8) & 0xF, i & 0xF, (i >> 4) & 0xF); + } + } + + public int cardinality() { + return mask.cardinality(); + } + + /** + * {@return a view of this mask as a short collection} Used for updating MC internals. + */ + public ShortCollection asShortCollection() { + return new AbstractShortCollection() { + @Override + public ShortIterator iterator() { + return new ShortIterator() { + private int next = mask.nextSetBit(0); + + @Override public short nextShort() { + if (!hasNext()) { + throw new IllegalStateException(); + } + // Uses the fact that we share the order with SectionPos to efficiently map + short value = (short) next; + next = mask.nextSetBit(next + 1); + return value; + } + + @Override public boolean hasNext() { + return next >= 0; + } + }; + } + + @Override + public int size() { + return mask.cardinality(); + } + }; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeAdapter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeAdapter.java new file mode 100644 index 0000000000..20c17dd920 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeAdapter.java @@ -0,0 +1,34 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.wna; + +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.block.BlockState; + +/** + * Methods for adapting data to the native types. + */ +public interface NativeAdapter { + NativeBlockState toNative(BlockState state); + + BlockState fromNative(NativeBlockState state); + + NativePosition newBlockPos(BlockVector3 pos); +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeBlockState.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeBlockState.java new file mode 100644 index 0000000000..2770a1926f --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeBlockState.java @@ -0,0 +1,33 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.wna; + +/** + * The equivalent of {@link com.sk89q.worldedit.world.block.BlockState}, but in the platform's base. + */ +public interface NativeBlockState { + boolean isSame(NativeBlockState other); + + boolean isSameBlockType(NativeBlockState other); + + boolean hasBlockEntity(); + + NativeBlockState updateFromNeighbourShapes(NativeWorld world, NativePosition position); +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeChunk.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeChunk.java new file mode 100644 index 0000000000..64ac436bd9 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeChunk.java @@ -0,0 +1,71 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.wna; + +import com.sk89q.worldedit.internal.util.collection.ChunkSectionMask; + +import javax.annotation.Nullable; + +/** + * Represents a chunk in the world. Made of {@link NativeChunkSection PlatformChunkSections}. + */ +public interface NativeChunk { + NativeWorld getWorld(); + + boolean isTicking(); + + NativePosition getWorldPos(int offsetX, int offsetY, int offsetZ); + + NativeBlockState getBlockState(NativePosition blockPos); + + @Nullable + NativeBlockState setBlockState(NativePosition blockPos, NativeBlockState newState, boolean update); + + void markSectionChanged(int index, ChunkSectionMask changed); + + void updateHeightmaps(); + + void updateLightingForSectionAirChange(int index, boolean onlyAir); + + void removeSectionBlockEntity(int chunkX, int chunkY, int chunkZ); + + void initializeBlockEntity(int chunkX, int chunkY, int chunkZ, NativeBlockState newState); + + /** + * Get the chunk section at the given index. + * + * @param index the index, from 0 to the max height divided by 16 + * @return the chunk section + */ + NativeChunkSection getChunkSection(int index); + + /** + * Replaces a chunk section in the given chunk. This method is also responsible for updating heightmaps + * and creating block entities, to keep consistency with {@link #setBlockState(NativePosition, NativeBlockState, boolean)} + * (the method we used to use). This is usually easily done by calling + * {@link WNASharedImpl#postChunkSectionReplacement(NativeChunk, int, NativeChunkSection, NativeChunkSection, ChunkSectionMask)}. + * + * @param index the index, from 0 to the max height divided by 16 + * @param section the new chunk section + * @param modifiedBlocks the mask of modified blocks + * @return the old chunk section + */ + NativeChunkSection setChunkSection(int index, NativeChunkSection section, ChunkSectionMask modifiedBlocks); +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeChunkSection.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeChunkSection.java new file mode 100644 index 0000000000..5e93e057cd --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeChunkSection.java @@ -0,0 +1,68 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.wna; + +import com.sk89q.worldedit.world.registry.BlockMaterial; + +/** + * Represents a 16x16x16 section of a chunk. + * + *

+ * Mapped onto the platform representation of a chunk section. However, unlike the platform representation, this + * interface is not thread-safe, as it is intended to be used in a single-threaded context. + * Internally this uses the platform representation's unsafe methods. + *

+ */ +public interface NativeChunkSection { + /** + * Set a block in the section. + * + * @param i the x-coordinate, 0-15 + * @param j the y-coordinate, 0-15 + * @param k the z-coordinate, 0-15 + * @param blockState the block state + * @return the old block state + */ + NativeBlockState getThenSetBlock(int i, int j, int k, NativeBlockState blockState); + + /** + * Get a block in the section. + * + * @param i the x-coordinate, 0-15 + * @param j the y-coordinate, 0-15 + * @param k the z-coordinate, 0-15 + * @return the block state + */ + NativeBlockState getBlock(int i, int j, int k); + + /** + * Get if this section is made of only air (specifically, {@link BlockMaterial#isAir()}). + * + * @return true if the section is only air + */ + boolean isOnlyAir(); + + /** + * Copy the section. + * + * @return the copy + */ + NativeChunkSection copy(); +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativePosition.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativePosition.java new file mode 100644 index 0000000000..02ae2ebcb7 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativePosition.java @@ -0,0 +1,31 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.wna; + +/** + * The equivalent of {@link com.sk89q.worldedit.math.BlockVector3}, but in the platform's base. + */ +public interface NativePosition { + int x(); + + int y(); + + int z(); +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeWorld.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeWorld.java new file mode 100644 index 0000000000..c287b0afb9 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeWorld.java @@ -0,0 +1,56 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.wna; + +import org.enginehub.linbus.tree.LinCompoundTag; + +/** + * The equivalent of {@link com.sk89q.worldedit.world.World}, but in the platform's base. + */ +public interface NativeWorld { + /** + * Hacky way to get the adapter. Ideally would be part of an internal platform API or something. + * + * @return the adapter + */ + NativeAdapter getAdapter(); + + int getSectionIndex(int y); + + int getYForSectionIndex(int index); + + NativeChunk getChunk(int chunkX, int chunkZ); + + void notifyBlockUpdate(NativePosition pos, NativeBlockState oldState, NativeBlockState newState); + + void markBlockChanged(NativePosition pos); + + void updateLightingForBlock(NativePosition position); + + boolean updateTileEntity(NativePosition position, LinCompoundTag tag); + + void notifyNeighbors(NativePosition pos, NativeBlockState oldState, NativeBlockState newState, boolean events); + + void updateBlock(NativePosition pos, NativeBlockState oldState, NativeBlockState newState); + + void updateNeighbors(NativePosition pos, NativeBlockState oldState, NativeBlockState newState, int recursionLimit, boolean events); + + void onBlockStateChange(NativePosition pos, NativeBlockState oldState, NativeBlockState newState); +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WNASharedImpl.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WNASharedImpl.java new file mode 100644 index 0000000000..84167791f3 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WNASharedImpl.java @@ -0,0 +1,204 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.wna; + +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.internal.util.collection.ChunkSectionMask; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import org.enginehub.linbus.tree.LinCompoundTag; +import org.enginehub.linbus.tree.LinStringTag; +import org.enginehub.linbus.tree.LinTagType; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Shared implementation bits of {@link NativeWorld}. Platforms may call into this to use common code. + */ +public class WNASharedImpl { + public static > boolean setBlock( + NativeWorld nativeWorld, BlockVector3 position, B block, SideEffectSet sideEffects + ) throws WorldEditException { + checkNotNull(position); + checkNotNull(block); + + // First set the block + NativeChunk chunk = nativeWorld.getChunk(position.x() >> 4, position.z() >> 4); + NativePosition pos = nativeWorld.getAdapter().newBlockPos(position); + NativeBlockState old = chunk.getBlockState(pos); + NativeBlockState newState = nativeWorld.getAdapter().toNative(block.toImmutableState()); + // change block prior to placing if it should be fixed + if (sideEffects.shouldApply(SideEffect.VALIDATION)) { + newState = newState.updateFromNeighbourShapes(nativeWorld, pos); + } + NativeBlockState lastValue = chunk.setBlockState(pos, newState, sideEffects.shouldApply(SideEffect.UPDATE)); + boolean successful = lastValue != null; + + // Create the TileEntity + if (successful || old == newState) { + if (block instanceof BaseBlock baseBlock) { + successful |= updateTileEntity(nativeWorld, baseBlock.getNbt(), pos); + } + } + + if (successful) { + if (sideEffects.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { + nativeWorld.updateLightingForBlock(pos); + } + markAndNotifyBlock(nativeWorld, pos, chunk, old, newState, sideEffects); + } + + return successful; + } + + public static boolean updateTileEntity(NativeWorld nativeWorld, LinCompoundTag tag, NativePosition pos) { + if (tag == null) { + return false; + } + LinCompoundTag.Builder tagBuilder = tag.toBuilder() + .putInt("x", pos.x()) + .putInt("y", pos.y()) + .putInt("z", pos.z()); + String nbtId = extractNbtId(tag); + if (!nbtId.isBlank()) { + tagBuilder.putString("id", nbtId); + } + tag = tagBuilder.build(); + + // update if TE changed as well + return nativeWorld.updateTileEntity(pos, tag); + } + + public static String extractNbtId(LinCompoundTag tag) { + LinStringTag idTag = tag.findTag("id", LinTagType.stringTag()); + return idTag != null ? idTag.value() : ""; + } + + public static void applySideEffects( + NativeWorld nativeWorld, SideEffectSet sideEffectSet, BlockVector3 position, BlockState previousType + ) { + NativePosition pos = nativeWorld.getAdapter().newBlockPos(position); + NativeChunk chunk = nativeWorld.getChunk(position.x() >> 4, position.z() >> 4); + NativeBlockState oldData = nativeWorld.getAdapter().toNative(previousType); + NativeBlockState newData = chunk.getBlockState(pos); + + applySideEffectsNoLookups(nativeWorld, chunk, sideEffectSet, pos, oldData, newData); + } + + public static void applySideEffectsNoLookups( + NativeWorld nativeWorld, NativeChunk chunk, SideEffectSet sideEffectSet, NativePosition pos, + NativeBlockState oldData, NativeBlockState newData + ) { + if (sideEffectSet.shouldApply(SideEffect.UPDATE)) { + nativeWorld.updateBlock(pos, oldData, newData); + } + + if (sideEffectSet.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { + nativeWorld.updateLightingForBlock(pos); + } + + markAndNotifyBlock(nativeWorld, pos, chunk, oldData, newData, sideEffectSet); + } + + /** + * This is a heavily modified function stripped from MC to apply WorldEdit-modifications. + * + *

+ * See NeoForge's Level.markAndNotifyBlock + *

+ */ + public static void markAndNotifyBlock( + NativeWorld nativeWorld, NativePosition pos, NativeChunk chunk, NativeBlockState oldState, NativeBlockState newState, + SideEffectSet sideEffectSet + ) { + // Removed redundant branches + + if (chunk.isTicking()) { + if (sideEffectSet.shouldApply(SideEffect.ENTITY_AI)) { + nativeWorld.notifyBlockUpdate(pos, oldState, newState); + } else if (sideEffectSet.shouldApply(SideEffect.NETWORK)) { + // If we want to skip entity AI, just mark the block for sending + nativeWorld.markBlockChanged(pos); + } + } + + if (sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) { + nativeWorld.notifyNeighbors(pos, oldState, newState, sideEffectSet.shouldApply(SideEffect.EVENTS)); + } + + // Make connection updates optional + if (sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) { + nativeWorld.updateNeighbors(pos, oldState, newState, 512, sideEffectSet.shouldApply(SideEffect.EVENTS)); + } + + // Seems used only for PoI updates + if (sideEffectSet.shouldApply(SideEffect.POI_UPDATE)) { + nativeWorld.onBlockStateChange(pos, oldState, newState); + } + } + + /** + * After a chunk section replacement, this function can be called to update the heightmaps, block entities, etc. + * to keep consistency with {@link NativeChunk#setBlockState(NativePosition, NativeBlockState, boolean)}. Doing this allows + * skipping redundant updates caused by multiple set calls, and filtering out unwanted side effects. + * + * @param chunk the chunk + * @param index the replaced section index + * @param oldSection the old section + * @param newSection the new section + * @param modifiedBlocks the mask of modified blocks + */ + public static void postChunkSectionReplacement( + NativeChunk chunk, int index, NativeChunkSection oldSection, NativeChunkSection newSection, + ChunkSectionMask modifiedBlocks + ) { + modifiedBlocks.forEach((secX, secY, secZ) -> { + NativeBlockState oldState = oldSection.getBlock(secX, secY, secZ); + NativeBlockState newState = newSection.getBlock(secX, secY, secZ); + int chunkY = chunk.getWorld().getYForSectionIndex(index) + secY; + // We skip heightmaps, they're optimized at a higher level to a single call. + + // We skip onRemove here, will call in UPDATE side effect if necessary. + + if (!oldState.isSameBlockType(newState) && oldState.hasBlockEntity()) { + chunk.removeSectionBlockEntity(secX, chunkY, secZ); + } + + // We skip onPlace here, will call in UPDATE side effect if necessary. + + if (newState.hasBlockEntity()) { + chunk.initializeBlockEntity(secX, chunkY, secZ, newState); + } + }); + + boolean wasOnlyAir = oldSection.isOnlyAir(); + boolean onlyAir = newSection.isOnlyAir(); + if (wasOnlyAir != onlyAir) { + chunk.updateLightingForSectionAirChange(index, onlyAir); + } + } + + private WNASharedImpl() { + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java deleted file mode 100644 index 52d64cb130..0000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.internal.wna; - -import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.util.SideEffect; -import com.sk89q.worldedit.util.SideEffectSet; -import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.world.block.BlockStateHolder; -import org.enginehub.linbus.tree.LinCompoundTag; - -import javax.annotation.Nullable; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Natively access and perform operations on the world. - * - * @param the native chunk type - * @param the native block state type - * @param the native position type - */ -public interface WorldNativeAccess { - - default > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { - checkNotNull(position); - checkNotNull(block); - setCurrentSideEffectSet(sideEffects); - - int x = position.x(); - int y = position.y(); - int z = position.z(); - - // First set the block - NC chunk = getChunk(x >> 4, z >> 4); - NP pos = getPosition(x, y, z); - NBS old = getBlockState(chunk, pos); - NBS newState = toNative(block.toImmutableState()); - // change block prior to placing if it should be fixed - if (sideEffects.shouldApply(SideEffect.VALIDATION)) { - newState = getValidBlockForPosition(newState, pos); - } - NBS lastValue = setBlockState(chunk, pos, newState); - boolean successful = lastValue != null; - - // Create the TileEntity - if (successful || old == newState) { - if (block instanceof BaseBlock baseBlock) { - LinCompoundTag tag = baseBlock.getNbt(); - if (tag != null) { - LinCompoundTag.Builder tagBuilder = tag.toBuilder() - .putInt("x", position.x()) - .putInt("y", position.y()) - .putInt("z", position.z()); - if (!baseBlock.getNbtId().isBlank()) { - tagBuilder.putString("id", baseBlock.getNbtId()); - } - tag = tagBuilder.build(); - - // update if TE changed as well - successful = updateTileEntity(pos, tag); - } - } - } - - if (successful) { - if (sideEffects.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { - updateLightingForBlock(pos); - } - markAndNotifyBlock(pos, chunk, old, newState, sideEffects); - } - - return successful; - } - - default void applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) { - setCurrentSideEffectSet(sideEffectSet); - NP pos = getPosition(position.x(), position.y(), position.z()); - NC chunk = getChunk(position.x() >> 4, position.z() >> 4); - NBS oldData = toNative(previousType); - NBS newData = getBlockState(chunk, pos); - - if (sideEffectSet.shouldApply(SideEffect.UPDATE)) { - updateBlock(pos, oldData, newData); - } - - if (sideEffectSet.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { - updateLightingForBlock(pos); - } - - markAndNotifyBlock(pos, chunk, oldData, newData, sideEffectSet); - } - - // state-keeping functions for WNA - // may be thread-unsafe, as this is single-threaded code - - /** - * Receive the current side-effect set from the high level call. - * - *

- * This allows the implementation to branch on the side-effects internally. - *

- * - * @param sideEffectSet the set of side-effects - */ - default void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { - } - - // access functions - - NC getChunk(int x, int z); - - NBS toNative(BlockState state); - - NBS getBlockState(NC chunk, NP position); - - @Nullable - NBS setBlockState(NC chunk, NP position, NBS state); - - NBS getValidBlockForPosition(NBS block, NP position); - - NP getPosition(int x, int y, int z); - - void updateLightingForBlock(NP position); - - boolean updateTileEntity(NP position, LinCompoundTag tag); - - void notifyBlockUpdate(NC chunk, NP position, NBS oldState, NBS newState); - - boolean isChunkTicking(NC chunk); - - void markBlockChanged(NC chunk, NP position); - - void notifyNeighbors(NP pos, NBS oldState, NBS newState); - - default void updateBlock(NP pos, NBS oldState, NBS newState) { - } - - void updateNeighbors(NP pos, NBS oldState, NBS newState, int recursionLimit); - - void onBlockStateChange(NP pos, NBS oldState, NBS newState); - - /** - * This is a heavily modified function stripped from MC to apply WorldEdit-modifications. - * - *

- * See Forge's World.markAndNotifyBlock - *

- */ - default void markAndNotifyBlock(NP pos, NC chunk, NBS oldState, NBS newState, SideEffectSet sideEffectSet) { - NBS blockState1 = getBlockState(chunk, pos); - if (blockState1 != newState) { - return; - } - - // Remove redundant branches - if (isChunkTicking(chunk)) { - if (sideEffectSet.shouldApply(SideEffect.ENTITY_AI)) { - notifyBlockUpdate(chunk, pos, oldState, newState); - } else if (sideEffectSet.shouldApply(SideEffect.NETWORK)) { - // If we want to skip entity AI, just mark the block for sending - markBlockChanged(chunk, pos); - } - } - - if (sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) { - notifyNeighbors(pos, oldState, newState); - } - - // Make connection updates optional - if (sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) { - updateNeighbors(pos, oldState, newState, 512); - } - - // Seems used only for PoI updates - if (sideEffectSet.shouldApply(SideEffect.POI_UPDATE)) { - onBlockStateChange(pos, oldState, blockState1); - } - } - -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/package-info.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/package-info.java index 182a404487..c7ea6d325c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/package-info.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/package-info.java @@ -21,7 +21,8 @@ * "WNA", or WorldEdit Native Access. * *

- * Contains internal helper functions for sharing code between platforms. + * Contains internal helper functions for sharing code between platforms. "Native*" interfaces are wrapped around or + * mixed in to the native structures. *

*/ package com.sk89q.worldedit.internal.wna; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java index a051594a2c..2332e4bfb9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.math; +import com.sk89q.worldedit.internal.wna.NativePosition; import com.sk89q.worldedit.math.transform.AffineTransform; import java.util.Comparator; @@ -32,7 +33,8 @@ /** * An immutable 3-dimensional vector. */ -public record BlockVector3(int x, int y, int z) { +// This implements the internal `NativePosition` interface so that Bukkit can use this as its native position type +public record BlockVector3(int x, int y, int z) implements NativePosition { public static final BlockVector3 ZERO = new BlockVector3(0, 0, 0); public static final BlockVector3 UNIT_X = new BlockVector3(1, 0, 0); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java index 27ecf063a1..1c23e2b1d5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java @@ -134,6 +134,7 @@ public void load() { extendedYLimit = getBool("extended-y-limit", extendedYLimit); setDefaultLocaleName(getString("default-locale", defaultLocaleName)); commandBlockSupport = getBool("command-block-support", commandBlockSupport); + chunkSectionEditing = getBool("chunk-section-editing", chunkSectionEditing); LocalSession.MAX_HISTORY_SIZE = Math.max(15, getInt("history-size", 15)); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java index f2bea3c107..e1e6768b5b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java @@ -131,6 +131,8 @@ public void load() { setDefaultLocaleName(config.getString("default-locale", defaultLocaleName)); commandBlockSupport = config.getBoolean("command-block-support", false); + + chunkSectionEditing = config.getBoolean("chunk-section-editing", chunkSectionEditing); } public void unload() { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java index 14c607fa4b..07cb56177d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java @@ -26,6 +26,7 @@ import com.sk89q.worldedit.function.mask.BlockTypeMask; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.internal.wna.NativeWorld; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; @@ -49,6 +50,13 @@ public abstract class AbstractWorld implements World { private final PriorityQueue effectQueue = new PriorityQueue<>(); private int taskId = -1; + /** + * {@return the native interface to the world} Internal use only. + */ + public @Nullable NativeWorld getNativeInterface() { + return null; + } + @Override public boolean useItem(BlockVector3 position, BaseItem item, Direction face) { return false; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java index 609f7c7e26..a870b3dda6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java @@ -21,12 +21,11 @@ import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.blocks.TileEntityBlock; +import com.sk89q.worldedit.internal.wna.WNASharedImpl; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.concurrency.LazyReference; import org.enginehub.linbus.format.snbt.LinStringIO; import org.enginehub.linbus.tree.LinCompoundTag; -import org.enginehub.linbus.tree.LinStringTag; -import org.enginehub.linbus.tree.LinTagType; import java.util.Map; import java.util.Objects; @@ -120,8 +119,7 @@ public String getNbtId() { if (nbtData == null) { return ""; } - LinStringTag idTag = nbtData.getValue().findTag("id", LinTagType.stringTag()); - return idTag != null ? idTag.value() : ""; + return WNASharedImpl.extractNbtId(nbtData.getValue()); } @Nullable diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java index e5c2e7882b..33ce0f3c8e 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java @@ -25,6 +25,9 @@ import com.sk89q.worldedit.fabric.internal.FabricTransmogrifier; import com.sk89q.worldedit.fabric.internal.NBTConverter; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.NativeAdapter; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativePosition; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.registry.state.Property; @@ -68,6 +71,27 @@ public final class FabricAdapter { + private static final NativeAdapter NATIVE_ADAPTER = new NativeAdapter() { + @Override + public NativeBlockState toNative(BlockState state) { + return (NativeBlockState) FabricAdapter.adapt(state); + } + + @Override + public BlockState fromNative(NativeBlockState state) { + return FabricAdapter.adapt((net.minecraft.world.level.block.state.BlockState) state); + } + + @Override + public NativePosition newBlockPos(BlockVector3 pos) { + return (NativePosition) new BlockPos(pos.x(), pos.y(), pos.z()); + } + }; + + public static NativeAdapter asNativeAdapter() { + return NATIVE_ADAPTER; + } + private FabricAdapter() { } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java index 259d00f356..f0b51fccb7 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java @@ -38,11 +38,12 @@ import com.sk89q.worldedit.fabric.internal.ExtendedMinecraftServer; import com.sk89q.worldedit.fabric.internal.FabricEntity; import com.sk89q.worldedit.fabric.internal.FabricServerLevelDelegateProxy; -import com.sk89q.worldedit.fabric.internal.FabricWorldNativeAccess; import com.sk89q.worldedit.fabric.internal.NBTConverter; import com.sk89q.worldedit.function.mask.AbstractExtentMask; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.internal.Constants; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import com.sk89q.worldedit.internal.wna.WNASharedImpl; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; @@ -149,7 +150,6 @@ private static ResourceLocation getDimensionRegistryKey(Level world) { } private final WeakReference worldRef; - private final FabricWorldNativeAccess worldNativeAccess; /** * Construct a new world. @@ -159,7 +159,11 @@ private static ResourceLocation getDimensionRegistryKey(Level world) { FabricWorld(Level world) { checkNotNull(world); this.worldRef = new WeakReference<>(world); - this.worldNativeAccess = new FabricWorldNativeAccess(worldRef); + } + + @Override + public NativeWorld getNativeInterface() { + return (NativeWorld) getWorld(); } /** @@ -199,12 +203,12 @@ public Path getStoragePath() { @Override public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { clearContainerBlockContents(position); - return worldNativeAccess.setBlock(position, block, sideEffects); + return WNASharedImpl.setBlock(getNativeInterface(), position, block, sideEffects); } @Override public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) { - worldNativeAccess.applySideEffects(position, previousType, sideEffectSet); + WNASharedImpl.applySideEffects(getNativeInterface(), sideEffectSet, position, previousType); return Sets.intersection(FabricWorldEdit.inst.getPlatform().getSupportedSideEffects(), sideEffectSet.getSideEffectsToApply()); } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricWorldNativeAccess.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricWorldNativeAccess.java deleted file mode 100644 index 55695ac166..0000000000 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricWorldNativeAccess.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.fabric.internal; - -import com.sk89q.worldedit.fabric.FabricAdapter; -import com.sk89q.worldedit.internal.block.BlockStateIdAccess; -import com.sk89q.worldedit.internal.wna.WorldNativeAccess; -import com.sk89q.worldedit.util.SideEffect; -import com.sk89q.worldedit.util.SideEffectSet; -import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.level.FullChunkStatus; -import net.minecraft.server.level.ServerChunkCache; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.LevelChunk; -import org.enginehub.linbus.tree.LinCompoundTag; - -import java.lang.ref.WeakReference; -import java.util.Objects; -import javax.annotation.Nullable; - -public class FabricWorldNativeAccess implements WorldNativeAccess { - private static final int UPDATE = 1; - private static final int NOTIFY = 2; - - private final WeakReference world; - private SideEffectSet sideEffectSet; - - public FabricWorldNativeAccess(WeakReference world) { - this.world = world; - } - - private Level getWorld() { - return Objects.requireNonNull(world.get(), "The reference to the world was lost"); - } - - @Override - public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { - this.sideEffectSet = sideEffectSet; - } - - @Override - public LevelChunk getChunk(int x, int z) { - return getWorld().getChunk(x, z); - } - - @Override - public BlockState toNative(com.sk89q.worldedit.world.block.BlockState state) { - int stateId = BlockStateIdAccess.getBlockStateId(state); - return BlockStateIdAccess.isValidInternalId(stateId) - ? Block.stateById(stateId) - : FabricAdapter.adapt(state); - } - - @Override - public BlockState getBlockState(LevelChunk chunk, BlockPos position) { - return chunk.getBlockState(position); - } - - @Nullable - @Override - public BlockState setBlockState(LevelChunk chunk, BlockPos position, BlockState state) { - if (chunk instanceof ExtendedChunk) { - return ((ExtendedChunk) chunk).setBlockState( - position, state, false, sideEffectSet.shouldApply(SideEffect.UPDATE) - ); - } - return chunk.setBlockState(position, state, false); - } - - @Override - public BlockState getValidBlockForPosition(BlockState block, BlockPos position) { - return Block.updateFromNeighbourShapes(block, getWorld(), position); - } - - @Override - public BlockPos getPosition(int x, int y, int z) { - return new BlockPos(x, y, z); - } - - @Override - public void updateLightingForBlock(BlockPos position) { - getWorld().getChunkSource().getLightEngine().checkBlock(position); - } - - @Override - public boolean updateTileEntity(BlockPos position, LinCompoundTag tag) { - CompoundTag nativeTag = NBTConverter.toNative(tag); - Level level = getWorld(); - BlockEntity tileEntity = level.getChunkAt(position).getBlockEntity(position); - if (tileEntity == null) { - return false; - } - tileEntity.loadWithComponents(nativeTag, level.registryAccess()); - tileEntity.setChanged(); - return true; - } - - @Override - public void notifyBlockUpdate(LevelChunk chunk, BlockPos position, BlockState oldState, BlockState newState) { - if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { - getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY); - } - } - - @Override - public boolean isChunkTicking(LevelChunk chunk) { - return chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING); - } - - @Override - public void markBlockChanged(LevelChunk chunk, BlockPos position) { - if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { - ((ServerChunkCache) getWorld().getChunkSource()).blockChanged(position); - } - } - - @Override - public void notifyNeighbors(BlockPos pos, BlockState oldState, BlockState newState) { - getWorld().blockUpdated(pos, oldState.getBlock()); - if (newState.hasAnalogOutputSignal()) { - getWorld().updateNeighbourForOutputSignal(pos, newState.getBlock()); - } - } - - @Override - public void updateBlock(BlockPos pos, BlockState oldState, BlockState newState) { - Level world = getWorld(); - newState.onPlace(world, pos, oldState, false); - } - - @Override - public void updateNeighbors(BlockPos pos, BlockState oldState, BlockState newState, int recursionLimit) { - Level world = getWorld(); - oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); - newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit); - newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); - } - - @Override - public void onBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) { - getWorld().onBlockStateChange(pos, oldState, newState); - } -} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeBlockState.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeBlockState.java new file mode 100644 index 0000000000..5204010baa --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeBlockState.java @@ -0,0 +1,63 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric.mixin; + +import com.mojang.serialization.MapCodec; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.Property; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(BlockState.class) +@Implements(@Interface(iface = NativeBlockState.class, prefix = "nbs$")) +public abstract class MixinNativeBlockState extends BlockBehaviour.BlockStateBase { + protected MixinNativeBlockState(Block owner, Reference2ObjectArrayMap, Comparable> values, MapCodec propertiesCodec) { + super(owner, values, propertiesCodec); + } + + public boolean nbs$isSame(NativeBlockState other) { + return this == other; + } + + public boolean nbs$isSameBlockType(NativeBlockState other) { + return this.getBlock() == ((BlockState) other).getBlock(); + } + + public boolean nbs$hasBlockEntity() { + return super.hasBlockEntity(); + } + + public NativeBlockState nbs$updateFromNeighbourShapes( + NativeWorld world, NativePosition position + ) { + return (NativeBlockState) Block.updateFromNeighbourShapes( + (BlockState) (Object) this, (LevelAccessor) world, (BlockPos) position + ); + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeChunk.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeChunk.java new file mode 100644 index 0000000000..b654b85173 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeChunk.java @@ -0,0 +1,183 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric.mixin; + +import com.google.common.base.Preconditions; +import com.sk89q.worldedit.fabric.internal.ExtendedChunk; +import com.sk89q.worldedit.internal.util.collection.ChunkSectionMask; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativeChunk; +import com.sk89q.worldedit.internal.wna.NativeChunkSection; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import com.sk89q.worldedit.internal.wna.WNASharedImpl; +import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Registry; +import net.minecraft.core.SectionPos; +import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.FullChunkStatus; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.UpgradeData; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.blending.BlendingData; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +import java.util.EnumSet; +import java.util.Set; +import javax.annotation.Nullable; + +@Mixin(LevelChunk.class) +@Implements(@Interface(iface = NativeChunk.class, prefix = "nc$")) +public abstract class MixinNativeChunk extends ChunkAccess { + @Unique + private static final Set HEIGHTMAPS = EnumSet.of( + Heightmap.Types.WORLD_SURFACE, + Heightmap.Types.OCEAN_FLOOR, + Heightmap.Types.MOTION_BLOCKING, + Heightmap.Types.MOTION_BLOCKING_NO_LEAVES + ); + + public MixinNativeChunk(ChunkPos chunkPos, UpgradeData upgradeData, LevelHeightAccessor levelHeightAccessor, Registry biomeRegistry, long inhabitedTime, @org.jetbrains.annotations.Nullable LevelChunkSection[] sections, @org.jetbrains.annotations.Nullable BlendingData blendingData) { + super(chunkPos, upgradeData, levelHeightAccessor, biomeRegistry, inhabitedTime, sections, blendingData); + } + + @Shadow + public abstract FullChunkStatus getFullStatus(); + + @Shadow + public abstract BlockState getBlockState(BlockPos pos); + + @Shadow + public abstract @Nullable BlockState setBlockState(BlockPos pos, BlockState state, boolean moved); + + @Shadow + public abstract Level getLevel(); + + @Shadow + public abstract void removeBlockEntity(BlockPos pos); + + @Shadow + public abstract @Nullable BlockEntity getBlockEntity(BlockPos pos, LevelChunk.EntityCreationType creationType); + + @Shadow + public abstract void addAndRegisterBlockEntity(BlockEntity blockEntity); + + @Shadow + protected abstract void updateBlockEntityTicker(T blockEntity); + + @Shadow public abstract void markUnsaved(); + + public NativeWorld nc$getWorld() { + return (NativeWorld) getLevel(); + } + + public boolean nc$isTicking() { + return getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING); + } + + public NativePosition nc$getWorldPos(int offsetX, int offsetY, int offsetZ) { + return (NativePosition) getPos().getBlockAt(offsetX, offsetY, offsetZ); + } + + public NativeBlockState nc$getBlockState(NativePosition blockPos) { + return (NativeBlockState) getBlockState((BlockPos) blockPos); + } + + public @Nullable NativeBlockState nc$setBlockState(NativePosition blockPos, NativeBlockState newState, boolean update) { + return (NativeBlockState) ((ExtendedChunk) this).setBlockState( + (BlockPos) blockPos, (BlockState) newState, false, update + ); + } + + public void nc$markSectionChanged(int index, ChunkSectionMask changed) { + ServerChunkCache serverChunkCache = (ServerChunkCache) getLevel().getChunkSource(); + ChunkHolder holder = serverChunkCache.getVisibleChunkIfPresent(getPos().toLong()); + if (holder != null) { + if (holder.changedBlocksPerSection[index] == null) { + holder.hasChangedSections = true; + holder.changedBlocksPerSection[index] = new ShortOpenHashSet(changed.asShortCollection()); + } else { + holder.changedBlocksPerSection[index].addAll(changed.asShortCollection()); + } + // Trick to get the holder into the broadcast set + ((ServerChunkCache) getLevel().getChunkSource()).onChunkReadyToSend(holder); + } + } + + public void nc$updateHeightmaps() { + Heightmap.primeHeightmaps(this, HEIGHTMAPS); + } + + public void nc$updateLightingForSectionAirChange(int index, boolean onlyAir) { + getLevel().getLightEngine().updateSectionStatus( + SectionPos.of(getPos(), getLevel().getSectionYFromSectionIndex(index)), + onlyAir + ); + } + + public void nc$removeSectionBlockEntity(int chunkX, int chunkY, int chunkZ) { + removeBlockEntity(getPos().getBlockAt(chunkX, chunkY, chunkZ)); + } + + @SuppressWarnings("deprecation") + public void nc$initializeBlockEntity(int chunkX, int chunkY, int chunkZ, NativeBlockState newState) { + BlockPos pos = getPos().getBlockAt(chunkX, chunkY, chunkZ); + BlockEntity blockEntity = this.getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK); + BlockState nativeState = (BlockState) newState; + if (blockEntity == null) { + blockEntity = ((EntityBlock) nativeState.getBlock()).newBlockEntity(pos, nativeState); + if (blockEntity != null) { + this.addAndRegisterBlockEntity(blockEntity); + } + } else { + blockEntity.setBlockState(nativeState); + this.updateBlockEntityTicker(blockEntity); + } + } + + public NativeChunkSection nc$getChunkSection(int index) { + return (NativeChunkSection) getSection(index); + } + + public NativeChunkSection nc$setChunkSection(int index, NativeChunkSection section, ChunkSectionMask modifiedBlocks) { + Preconditions.checkPositionIndex(index, getSectionsCount()); + LevelChunkSection[] chunkSections = getSections(); + var oldSection = (NativeChunkSection) chunkSections[index]; + chunkSections[index] = (LevelChunkSection) section; + WNASharedImpl.postChunkSectionReplacement((NativeChunk) this, index, oldSection, section, modifiedBlocks); + this.markUnsaved(); + return oldSection; + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeChunkSection.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeChunkSection.java new file mode 100644 index 0000000000..80b5c67b0b --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeChunkSection.java @@ -0,0 +1,82 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric.mixin; + +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativeChunkSection; +import net.minecraft.core.Holder; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.PalettedContainer; +import net.minecraft.world.level.chunk.PalettedContainerRO; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(LevelChunkSection.class) +@Implements(@Interface(iface = NativeChunkSection.class, prefix = "ncs$")) +public abstract class MixinNativeChunkSection { + @Shadow + public abstract net.minecraft.world.level.block.state.BlockState setBlockState( + int x, int y, int z, net.minecraft.world.level.block.state.BlockState state, boolean lock + ); + + @Shadow + public abstract net.minecraft.world.level.block.state.BlockState getBlockState( + int x, int y, int z + ); + + @Final + @Mutable + @Shadow + private PalettedContainer states; + + @Shadow + private PalettedContainerRO> biomes; + + @Shadow + public abstract boolean hasOnlyAir(); + + public boolean ncs$isOnlyAir() { + return hasOnlyAir(); + } + + public NativeBlockState ncs$getThenSetBlock(int i, int j, int k, NativeBlockState blockState) { + BlockState nativeState = (BlockState) blockState; + if (ncs$isOnlyAir() && nativeState.isAir()) { + return blockState; + } + return (NativeBlockState) setBlockState(i, j, k, nativeState, false); + } + + public NativeBlockState ncs$getBlock(int i, int j, int k) { + return (NativeBlockState) getBlockState(i, j, k); + } + + public NativeChunkSection ncs$copy() { + return (NativeChunkSection) new LevelChunkSection( + states.copy(), biomes.copy() + ); + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativePosition.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativePosition.java new file mode 100644 index 0000000000..532176d783 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativePosition.java @@ -0,0 +1,47 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric.mixin; + +import com.sk89q.worldedit.internal.wna.NativePosition; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(BlockPos.class) +@Implements(@Interface(iface = NativePosition.class, prefix = "nc$")) +public abstract class MixinNativePosition extends Vec3i { + public MixinNativePosition(int x, int y, int z) { + super(x, y, z); + } + + public int nc$x() { + return getX(); + } + + public int nc$y() { + return getY(); + } + + public int nc$z() { + return getZ(); + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeWorld.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeWorld.java new file mode 100644 index 0000000000..3392e1a584 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeWorld.java @@ -0,0 +1,137 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric.mixin; + +import com.sk89q.worldedit.fabric.FabricAdapter; +import com.sk89q.worldedit.fabric.internal.NBTConverter; +import com.sk89q.worldedit.internal.wna.NativeAdapter; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativeChunk; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.SectionPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.profiling.Profiler; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.storage.WritableLevelData; +import org.enginehub.linbus.tree.LinCompoundTag; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(ServerLevel.class) +@Implements(@Interface(iface = NativeWorld.class, prefix = "nw$")) +public abstract class MixinNativeWorld extends Level { + public MixinNativeWorld(WritableLevelData writableLevelData, ResourceKey resourceKey, RegistryAccess registryAccess, Holder holder, boolean bl, boolean bl2, long l, int i) { + super(writableLevelData, resourceKey, registryAccess, holder, bl, bl2, l, i); + } + + public NativeAdapter nw$getAdapter() { + return FabricAdapter.asNativeAdapter(); + } + + public int nw$getSectionIndex(int y) { + return super.getSectionIndex(y); + } + + public int nw$getYForSectionIndex(int index) { + return SectionPos.sectionToBlockCoord(super.getSectionYFromSectionIndex(index)); + } + + public NativeChunk nw$getChunk(int chunkX, int chunkZ) { + return (NativeChunk) getChunk(chunkX, chunkZ); + } + + public void nw$notifyBlockUpdate(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { + sendBlockUpdated( + (BlockPos) pos, (BlockState) oldState, (BlockState) newState, + Block.UPDATE_NEIGHBORS | Block.UPDATE_CLIENTS + ); + } + + public void nw$markBlockChanged(NativePosition pos) { + ((ServerChunkCache) getChunkSource()).blockChanged((BlockPos) pos); + } + + public void nw$updateLightingForBlock(NativePosition position) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("updateSkyLightSources"); + getChunk(position.x() >> 4, position.z() >> 4).getSkyLightSources() + .update(this, position.x() & 0xF, position.y(), position.z() & 0xF); + profilerFiller.popPush("queueCheckLight"); + getChunkSource().getLightEngine().checkBlock((BlockPos) position); + profilerFiller.pop(); + } + + public boolean nw$updateTileEntity(NativePosition position, LinCompoundTag tag) { + CompoundTag nativeTag = NBTConverter.toNative(tag); + BlockPos nativePos = (BlockPos) position; + BlockEntity tileEntity = getChunkAt(nativePos).getBlockEntity(nativePos); + if (tileEntity == null) { + return false; + } + tileEntity.loadWithComponents(nativeTag, registryAccess()); + tileEntity.setChanged(); + return true; + } + + public void nw$notifyNeighbors(NativePosition pos, NativeBlockState oldState, NativeBlockState newState, boolean events) { + BlockPos nativePos = (BlockPos) pos; + blockUpdated(nativePos, ((BlockState) oldState).getBlock()); + BlockState nativeNewState = (BlockState) newState; + if (nativeNewState.hasAnalogOutputSignal()) { + updateNeighbourForOutputSignal(nativePos, nativeNewState.getBlock()); + } + } + + public void nw$updateBlock(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { + BlockPos nativePos = (BlockPos) pos; + BlockState nativeOldState = (BlockState) oldState; + BlockState nativeNewState = (BlockState) newState; + nativeOldState.onRemove(this, nativePos, nativeNewState, false); + nativeNewState.onPlace(this, nativePos, nativeOldState, false); + } + + public void nw$updateNeighbors( + NativePosition pos, NativeBlockState oldState, NativeBlockState newState, int recursionLimit, boolean events + ) { + BlockPos nativePos = (BlockPos) pos; + BlockState nativeOldState = (BlockState) oldState; + BlockState nativeNewState = (BlockState) newState; + nativeOldState.updateIndirectNeighbourShapes(this, nativePos, Block.UPDATE_CLIENTS, recursionLimit); + nativeNewState.updateNeighbourShapes(this, nativePos, Block.UPDATE_CLIENTS, recursionLimit); + nativeNewState.updateIndirectNeighbourShapes(this, nativePos, Block.UPDATE_CLIENTS, recursionLimit); + } + + public void nw$onBlockStateChange(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { + onBlockStateChange((BlockPos) pos, (BlockState) oldState, (BlockState) newState); + } +} diff --git a/worldedit-fabric/src/main/resources/worldedit-fabric.mixins.json b/worldedit-fabric/src/main/resources/worldedit-fabric.mixins.json index 232b19e65f..b8e6542f12 100644 --- a/worldedit-fabric/src/main/resources/worldedit-fabric.mixins.json +++ b/worldedit-fabric/src/main/resources/worldedit-fabric.mixins.json @@ -5,11 +5,14 @@ "mixins": [ "MixinLevelChunkSetBlockHook", "MixinMinecraftServer", + "MixinNativeBlockState", + "MixinNativeChunk", + "MixinNativeChunkSection", + "MixinNativePosition", + "MixinNativeWorld", "MixinServerGamePacketListenerImpl" ], "plugin": "com.sk89q.worldedit.fabric.internal.MixinConfigPlugin", - "server": [ - ], "injectors": { "defaultRequire": 1 } diff --git a/worldedit-fabric/src/main/resources/worldedit.accesswidener b/worldedit-fabric/src/main/resources/worldedit.accesswidener index 88f77ce207..ee03b2c584 100644 --- a/worldedit-fabric/src/main/resources/worldedit.accesswidener +++ b/worldedit-fabric/src/main/resources/worldedit.accesswidener @@ -13,3 +13,8 @@ accessible field net/minecraft/world/level/storage/PrimaryLevelData worldOptions mutable field net/minecraft/world/level/storage/PrimaryLevelData worldOptions Lnet/minecraft/world/level/levelgen/WorldOptions; accessible method net/minecraft/network/protocol/game/ClientboundBlockEntityDataPacket (Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/entity/BlockEntityType;Lnet/minecraft/nbt/CompoundTag;)V + +# Ability to mark sections changed +accessible method net/minecraft/server/level/ServerChunkCache getVisibleChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder; +accessible field net/minecraft/server/level/ChunkHolder changedBlocksPerSection [Lit/unimi/dsi/fastutil/shorts/ShortSet; +accessible field net/minecraft/server/level/ChunkHolder hasChangedSections Z diff --git a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/NeoForgeAdapter.java b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/NeoForgeAdapter.java index 8706822486..0d34953e19 100644 --- a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/NeoForgeAdapter.java +++ b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/NeoForgeAdapter.java @@ -23,6 +23,9 @@ import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.NativeAdapter; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativePosition; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.neoforge.internal.NBTConverter; @@ -67,6 +70,26 @@ import static com.google.common.base.Preconditions.checkNotNull; public final class NeoForgeAdapter { + private static final NativeAdapter NATIVE_ADAPTER = new NativeAdapter() { + @Override + public NativeBlockState toNative(BlockState state) { + return (NativeBlockState) NeoForgeAdapter.adapt(state); + } + + @Override + public BlockState fromNative(NativeBlockState state) { + return NeoForgeAdapter.adapt((net.minecraft.world.level.block.state.BlockState) state); + } + + @Override + public NativePosition newBlockPos(BlockVector3 pos) { + return (NativePosition) new BlockPos(pos.x(), pos.y(), pos.z()); + } + }; + + public static NativeAdapter asNativeAdapter() { + return NATIVE_ADAPTER; + } private NeoForgeAdapter() { } diff --git a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/NeoForgeWorld.java b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/NeoForgeWorld.java index 1e764406f5..209c9f561b 100644 --- a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/NeoForgeWorld.java +++ b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/NeoForgeWorld.java @@ -38,13 +38,14 @@ import com.sk89q.worldedit.function.mask.AbstractExtentMask; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.internal.Constants; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import com.sk89q.worldedit.internal.wna.WNASharedImpl; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.neoforge.internal.NBTConverter; import com.sk89q.worldedit.neoforge.internal.NeoForgeEntity; import com.sk89q.worldedit.neoforge.internal.NeoForgeServerLevelDelegateProxy; -import com.sk89q.worldedit.neoforge.internal.NeoForgeWorldNativeAccess; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Location; @@ -145,7 +146,6 @@ private static ResourceLocation getDimensionRegistryKey(ServerLevel world) { } private final WeakReference worldRef; - private final NeoForgeWorldNativeAccess nativeAccess; /** * Construct a new world. @@ -155,7 +155,11 @@ private static ResourceLocation getDimensionRegistryKey(ServerLevel world) { NeoForgeWorld(ServerLevel world) { checkNotNull(world); this.worldRef = new WeakReference<>(world); - this.nativeAccess = new NeoForgeWorldNativeAccess(worldRef); + } + + @Override + public NativeWorld getNativeInterface() { + return (NativeWorld) getWorld(); } /** @@ -192,12 +196,12 @@ public Path getStoragePath() { @Override public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { clearContainerBlockContents(position); - return nativeAccess.setBlock(position, block, sideEffects); + return WNASharedImpl.setBlock(getNativeInterface(), position, block, sideEffects); } @Override public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) { - nativeAccess.applySideEffects(position, previousType, sideEffectSet); + WNASharedImpl.applySideEffects(getNativeInterface(), sideEffectSet, position, previousType); return Sets.intersection(NeoForgeWorldEdit.inst.getPlatform().getSupportedSideEffects(), sideEffectSet.getSideEffectsToApply()); } diff --git a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/internal/NeoForgeWorldNativeAccess.java b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/internal/NeoForgeWorldNativeAccess.java deleted file mode 100644 index 9c4ffd35db..0000000000 --- a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/internal/NeoForgeWorldNativeAccess.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.neoforge.internal; - -import com.sk89q.worldedit.internal.block.BlockStateIdAccess; -import com.sk89q.worldedit.internal.wna.WorldNativeAccess; -import com.sk89q.worldedit.neoforge.NeoForgeAdapter; -import com.sk89q.worldedit.util.SideEffect; -import com.sk89q.worldedit.util.SideEffectSet; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.FullChunkStatus; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.redstone.ExperimentalRedstoneUtils; -import org.enginehub.linbus.tree.LinCompoundTag; - -import java.lang.ref.WeakReference; -import java.util.Objects; -import javax.annotation.Nullable; - -public class NeoForgeWorldNativeAccess implements WorldNativeAccess { - private static final int UPDATE = 1; - private static final int NOTIFY = 2; - - private final WeakReference world; - private SideEffectSet sideEffectSet; - - public NeoForgeWorldNativeAccess(WeakReference world) { - this.world = world; - } - - private ServerLevel getWorld() { - return Objects.requireNonNull(world.get(), "The reference to the world was lost"); - } - - @Override - public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { - this.sideEffectSet = sideEffectSet; - } - - @Override - public LevelChunk getChunk(int x, int z) { - return getWorld().getChunk(x, z); - } - - @Override - public BlockState toNative(com.sk89q.worldedit.world.block.BlockState state) { - int stateId = BlockStateIdAccess.getBlockStateId(state); - return BlockStateIdAccess.isValidInternalId(stateId) - ? Block.stateById(stateId) - : NeoForgeAdapter.adapt(state); - } - - @Override - public BlockState getBlockState(LevelChunk chunk, BlockPos position) { - return chunk.getBlockState(position); - } - - @Nullable - @Override - public BlockState setBlockState(LevelChunk chunk, BlockPos position, BlockState state) { - if (chunk instanceof ExtendedChunk) { - return ((ExtendedChunk) chunk).setBlockState( - position, state, false, sideEffectSet.shouldApply(SideEffect.UPDATE) - ); - } - return chunk.setBlockState(position, state, false); - } - - @Override - public BlockState getValidBlockForPosition(BlockState block, BlockPos position) { - return Block.updateFromNeighbourShapes(block, getWorld(), position); - } - - @Override - public BlockPos getPosition(int x, int y, int z) { - return new BlockPos(x, y, z); - } - - @Override - public void updateLightingForBlock(BlockPos position) { - getWorld().getChunkSource().getLightEngine().checkBlock(position); - } - - @Override - public boolean updateTileEntity(BlockPos position, LinCompoundTag tag) { - net.minecraft.nbt.CompoundTag nativeTag = NBTConverter.toNative(tag); - Level level = getWorld(); - BlockEntity tileEntity = level.getChunkAt(position).getBlockEntity(position); - if (tileEntity == null) { - return false; - } - tileEntity.loadWithComponents(nativeTag, level.registryAccess()); - tileEntity.setChanged(); - return true; - } - - @Override - public void notifyBlockUpdate(LevelChunk chunk, BlockPos position, BlockState oldState, BlockState newState) { - if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { - getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY); - } - } - - @Override - public boolean isChunkTicking(LevelChunk chunk) { - return chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING); - } - - @Override - public void markBlockChanged(LevelChunk chunk, BlockPos position) { - if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { - getWorld().getChunkSource().blockChanged(position); - } - } - - @Override - public void notifyNeighbors(BlockPos pos, BlockState oldState, BlockState newState) { - ServerLevel world = getWorld(); - if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { - world.updateNeighborsAt(pos, oldState.getBlock()); - } else { - // Bypasses events currently, watch for changes... - world.updateNeighborsAt(pos, oldState.getBlock(), ExperimentalRedstoneUtils.initialOrientation( - world, null, null - )); - } - if (newState.hasAnalogOutputSignal()) { - world.updateNeighbourForOutputSignal(pos, newState.getBlock()); - } - } - - @Override - public void updateBlock(BlockPos pos, BlockState oldState, BlockState newState) { - ServerLevel world = getWorld(); - newState.onPlace(world, pos, oldState, false); - } - - @Override - public void updateNeighbors(BlockPos pos, BlockState oldState, BlockState newState, int recursionLimit) { - ServerLevel world = getWorld(); - oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); - newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit); - newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); - } - - @Override - public void onBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) { - getWorld().onBlockStateChange(pos, oldState, newState); - newState.onBlockStateChange(getWorld(), pos, oldState); - } -} diff --git a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeBlockState.java b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeBlockState.java new file mode 100644 index 0000000000..44bf463b0f --- /dev/null +++ b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeBlockState.java @@ -0,0 +1,63 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.neoforge.mixin; + +import com.mojang.serialization.MapCodec; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.Property; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(BlockState.class) +@Implements(@Interface(iface = NativeBlockState.class, prefix = "nbs$")) +public abstract class MixinNativeBlockState extends BlockBehaviour.BlockStateBase { + protected MixinNativeBlockState(Block owner, Reference2ObjectArrayMap, Comparable> values, MapCodec propertiesCodec) { + super(owner, values, propertiesCodec); + } + + public boolean nbs$isSame(NativeBlockState other) { + return this == other; + } + + public boolean nbs$isSameBlockType(NativeBlockState other) { + return this.getBlock() == ((BlockState) other).getBlock(); + } + + public boolean nbs$hasBlockEntity() { + return super.hasBlockEntity(); + } + + public NativeBlockState nbs$updateFromNeighbourShapes( + NativeWorld world, NativePosition position + ) { + return (NativeBlockState) Block.updateFromNeighbourShapes( + (BlockState) (Object) this, (LevelAccessor) world, (BlockPos) position + ); + } +} diff --git a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeChunk.java b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeChunk.java new file mode 100644 index 0000000000..a672da4237 --- /dev/null +++ b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeChunk.java @@ -0,0 +1,183 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.neoforge.mixin; + +import com.google.common.base.Preconditions; +import com.sk89q.worldedit.internal.util.collection.ChunkSectionMask; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativeChunk; +import com.sk89q.worldedit.internal.wna.NativeChunkSection; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import com.sk89q.worldedit.internal.wna.WNASharedImpl; +import com.sk89q.worldedit.neoforge.internal.ExtendedChunk; +import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Registry; +import net.minecraft.core.SectionPos; +import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.FullChunkStatus; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.UpgradeData; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.blending.BlendingData; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +import java.util.EnumSet; +import java.util.Set; +import javax.annotation.Nullable; + +@Mixin(LevelChunk.class) +@Implements(@Interface(iface = NativeChunk.class, prefix = "nc$")) +public abstract class MixinNativeChunk extends ChunkAccess { + @Unique + private static final Set HEIGHTMAPS = EnumSet.of( + Heightmap.Types.WORLD_SURFACE, + Heightmap.Types.OCEAN_FLOOR, + Heightmap.Types.MOTION_BLOCKING, + Heightmap.Types.MOTION_BLOCKING_NO_LEAVES + ); + + public MixinNativeChunk(ChunkPos chunkPos, UpgradeData upgradeData, LevelHeightAccessor levelHeightAccessor, Registry biomeRegistry, long inhabitedTime, @org.jetbrains.annotations.Nullable LevelChunkSection[] sections, @org.jetbrains.annotations.Nullable BlendingData blendingData) { + super(chunkPos, upgradeData, levelHeightAccessor, biomeRegistry, inhabitedTime, sections, blendingData); + } + + @Shadow + public abstract FullChunkStatus getFullStatus(); + + @Shadow + public abstract BlockState getBlockState(BlockPos pos); + + @Shadow + public abstract @Nullable BlockState setBlockState(BlockPos pos, BlockState state, boolean moved); + + @Shadow + public abstract Level getLevel(); + + @Shadow + public abstract void removeBlockEntity(BlockPos pos); + + @Shadow + public abstract @Nullable BlockEntity getBlockEntity(BlockPos pos, LevelChunk.EntityCreationType creationType); + + @Shadow + public abstract void addAndRegisterBlockEntity(BlockEntity blockEntity); + + @Shadow + protected abstract void updateBlockEntityTicker(T blockEntity); + + @Shadow public abstract void markUnsaved(); + + public NativeWorld nc$getWorld() { + return (NativeWorld) getLevel(); + } + + public boolean nc$isTicking() { + return getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING); + } + + public NativePosition nc$getWorldPos(int offsetX, int offsetY, int offsetZ) { + return (NativePosition) getPos().getBlockAt(offsetX, offsetY, offsetZ); + } + + public NativeBlockState nc$getBlockState(NativePosition blockPos) { + return (NativeBlockState) getBlockState((BlockPos) blockPos); + } + + public @Nullable NativeBlockState nc$setBlockState(NativePosition blockPos, NativeBlockState newState, boolean update) { + return (NativeBlockState) ((ExtendedChunk) this).setBlockState( + (BlockPos) blockPos, (BlockState) newState, false, update + ); + } + + public void nc$markSectionChanged(int index, ChunkSectionMask changed) { + ServerChunkCache serverChunkCache = (ServerChunkCache) getLevel().getChunkSource(); + ChunkHolder holder = serverChunkCache.getVisibleChunkIfPresent(getPos().toLong()); + if (holder != null) { + if (holder.changedBlocksPerSection[index] == null) { + holder.hasChangedSections = true; + holder.changedBlocksPerSection[index] = new ShortOpenHashSet(changed.asShortCollection()); + } else { + holder.changedBlocksPerSection[index].addAll(changed.asShortCollection()); + } + // Trick to get the holder into the broadcast set + ((ServerChunkCache) getLevel().getChunkSource()).onChunkReadyToSend(holder); + } + } + + public void nc$updateHeightmaps() { + Heightmap.primeHeightmaps(this, HEIGHTMAPS); + } + + public void nc$updateLightingForSectionAirChange(int index, boolean onlyAir) { + getLevel().getLightEngine().updateSectionStatus( + SectionPos.of(getPos(), getLevel().getSectionYFromSectionIndex(index)), + onlyAir + ); + } + + public void nc$removeSectionBlockEntity(int chunkX, int chunkY, int chunkZ) { + removeBlockEntity(getPos().getBlockAt(chunkX, chunkY, chunkZ)); + } + + @SuppressWarnings("deprecation") + public void nc$initializeBlockEntity(int chunkX, int chunkY, int chunkZ, NativeBlockState newState) { + BlockPos pos = getPos().getBlockAt(chunkX, chunkY, chunkZ); + BlockEntity blockEntity = this.getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK); + BlockState nativeState = (BlockState) newState; + if (blockEntity == null) { + blockEntity = ((EntityBlock) nativeState.getBlock()).newBlockEntity(pos, nativeState); + if (blockEntity != null) { + this.addAndRegisterBlockEntity(blockEntity); + } + } else { + blockEntity.setBlockState(nativeState); + this.updateBlockEntityTicker(blockEntity); + } + } + + public NativeChunkSection nc$getChunkSection(int index) { + return (NativeChunkSection) getSection(index); + } + + public NativeChunkSection nc$setChunkSection(int index, NativeChunkSection section, ChunkSectionMask modifiedBlocks) { + Preconditions.checkPositionIndex(index, getSectionsCount()); + LevelChunkSection[] chunkSections = getSections(); + var oldSection = (NativeChunkSection) chunkSections[index]; + chunkSections[index] = (LevelChunkSection) section; + WNASharedImpl.postChunkSectionReplacement((NativeChunk) this, index, oldSection, section, modifiedBlocks); + markUnsaved(); + return oldSection; + } +} diff --git a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeChunkSection.java b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeChunkSection.java new file mode 100644 index 0000000000..48ef30f917 --- /dev/null +++ b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeChunkSection.java @@ -0,0 +1,82 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.neoforge.mixin; + +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativeChunkSection; +import net.minecraft.core.Holder; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.PalettedContainer; +import net.minecraft.world.level.chunk.PalettedContainerRO; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(LevelChunkSection.class) +@Implements(@Interface(iface = NativeChunkSection.class, prefix = "ncs$")) +public abstract class MixinNativeChunkSection { + @Shadow + public abstract BlockState setBlockState( + int x, int y, int z, BlockState state, boolean lock + ); + + @Shadow + public abstract BlockState getBlockState( + int x, int y, int z + ); + + @Final + @Mutable + @Shadow + private PalettedContainer states; + + @Shadow + private PalettedContainerRO> biomes; + + @Shadow + public abstract boolean hasOnlyAir(); + + public boolean ncs$isOnlyAir() { + return hasOnlyAir(); + } + + public NativeBlockState ncs$getThenSetBlock(int i, int j, int k, NativeBlockState blockState) { + BlockState nativeState = (BlockState) blockState; + if (ncs$isOnlyAir() && nativeState.isAir()) { + return blockState; + } + return (NativeBlockState) setBlockState(i, j, k, nativeState, false); + } + + public NativeBlockState ncs$getBlock(int i, int j, int k) { + return (NativeBlockState) getBlockState(i, j, k); + } + + public NativeChunkSection ncs$copy() { + return (NativeChunkSection) new LevelChunkSection( + states.copy(), biomes.copy() + ); + } +} diff --git a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativePosition.java b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativePosition.java new file mode 100644 index 0000000000..68ae22b94b --- /dev/null +++ b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativePosition.java @@ -0,0 +1,47 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.neoforge.mixin; + +import com.sk89q.worldedit.internal.wna.NativePosition; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(BlockPos.class) +@Implements(@Interface(iface = NativePosition.class, prefix = "nc$")) +public abstract class MixinNativePosition extends Vec3i { + public MixinNativePosition(int x, int y, int z) { + super(x, y, z); + } + + public int nc$x() { + return getX(); + } + + public int nc$y() { + return getY(); + } + + public int nc$z() { + return getZ(); + } +} diff --git a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeWorld.java b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeWorld.java new file mode 100644 index 0000000000..b711388da0 --- /dev/null +++ b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeWorld.java @@ -0,0 +1,137 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.neoforge.mixin; + +import com.sk89q.worldedit.internal.wna.NativeAdapter; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativeChunk; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.internal.wna.NativeWorld; +import com.sk89q.worldedit.neoforge.NeoForgeAdapter; +import com.sk89q.worldedit.neoforge.internal.NBTConverter; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.SectionPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.profiling.Profiler; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.storage.WritableLevelData; +import org.enginehub.linbus.tree.LinCompoundTag; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(ServerLevel.class) +@Implements(@Interface(iface = NativeWorld.class, prefix = "nw$")) +public abstract class MixinNativeWorld extends Level { + public MixinNativeWorld(WritableLevelData writableLevelData, ResourceKey resourceKey, RegistryAccess registryAccess, Holder holder, boolean bl, boolean bl2, long l, int i) { + super(writableLevelData, resourceKey, registryAccess, holder, bl, bl2, l, i); + } + + public NativeAdapter nw$getAdapter() { + return NeoForgeAdapter.asNativeAdapter(); + } + + public int nw$getSectionIndex(int y) { + return super.getSectionIndex(y); + } + + public int nw$getYForSectionIndex(int index) { + return SectionPos.sectionToBlockCoord(super.getSectionYFromSectionIndex(index)); + } + + public NativeChunk nw$getChunk(int chunkX, int chunkZ) { + return (NativeChunk) getChunk(chunkX, chunkZ); + } + + public void nw$notifyBlockUpdate(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { + sendBlockUpdated( + (BlockPos) pos, (BlockState) oldState, (BlockState) newState, + Block.UPDATE_NEIGHBORS | Block.UPDATE_CLIENTS + ); + } + + public void nw$markBlockChanged(NativePosition pos) { + ((ServerChunkCache) getChunkSource()).blockChanged((BlockPos) pos); + } + + public void nw$updateLightingForBlock(NativePosition position) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("updateSkyLightSources"); + getChunk(position.x() >> 4, position.z() >> 4).getSkyLightSources() + .update(this, position.x() & 0xF, position.y(), position.z() & 0xF); + profilerFiller.popPush("queueCheckLight"); + getChunkSource().getLightEngine().checkBlock((BlockPos) position); + profilerFiller.pop(); + } + + public boolean nw$updateTileEntity(NativePosition position, LinCompoundTag tag) { + CompoundTag nativeTag = NBTConverter.toNative(tag); + BlockPos nativePos = (BlockPos) position; + BlockEntity tileEntity = getChunkAt(nativePos).getBlockEntity(nativePos); + if (tileEntity == null) { + return false; + } + tileEntity.loadWithComponents(nativeTag, registryAccess()); + tileEntity.setChanged(); + return true; + } + + public void nw$notifyNeighbors(NativePosition pos, NativeBlockState oldState, NativeBlockState newState, boolean events) { + BlockPos nativePos = (BlockPos) pos; + blockUpdated(nativePos, ((BlockState) oldState).getBlock()); + BlockState nativeNewState = (BlockState) newState; + if (nativeNewState.hasAnalogOutputSignal()) { + updateNeighbourForOutputSignal(nativePos, nativeNewState.getBlock()); + } + } + + public void nw$updateBlock(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { + BlockPos nativePos = (BlockPos) pos; + BlockState nativeOldState = (BlockState) oldState; + BlockState nativeNewState = (BlockState) newState; + nativeOldState.onRemove(this, nativePos, nativeNewState, false); + nativeNewState.onPlace(this, nativePos, nativeOldState, false); + } + + public void nw$updateNeighbors( + NativePosition pos, NativeBlockState oldState, NativeBlockState newState, int recursionLimit, boolean events + ) { + BlockPos nativePos = (BlockPos) pos; + BlockState nativeOldState = (BlockState) oldState; + BlockState nativeNewState = (BlockState) newState; + nativeOldState.updateIndirectNeighbourShapes(this, nativePos, Block.UPDATE_CLIENTS, recursionLimit); + nativeNewState.updateNeighbourShapes(this, nativePos, Block.UPDATE_CLIENTS, recursionLimit); + nativeNewState.updateIndirectNeighbourShapes(this, nativePos, Block.UPDATE_CLIENTS, recursionLimit); + } + + public void nw$onBlockStateChange(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { + onBlockStateChange((BlockPos) pos, (BlockState) oldState, (BlockState) newState); + } +} diff --git a/worldedit-neoforge/src/main/resources/META-INF/accesstransformer.cfg b/worldedit-neoforge/src/main/resources/META-INF/accesstransformer.cfg index 08401db51c..1939389b6c 100644 --- a/worldedit-neoforge/src/main/resources/META-INF/accesstransformer.cfg +++ b/worldedit-neoforge/src/main/resources/META-INF/accesstransformer.cfg @@ -8,3 +8,8 @@ public net.minecraft.world.level.chunk.ChunkBiomeContainer biomes public-f net.minecraft.world.level.storage.PrimaryLevelData worldOptions public net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket (Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/entity/BlockEntityType;Lnet/minecraft/nbt/CompoundTag;)V + +# Ability to mark sections changed +public net.minecraft.server.level.ServerChunkCache getVisibleChunkIfPresent(J)Lnet/minecraft/server/level/ChunkHolder; +public net.minecraft.server.level.ChunkHolder changedBlocksPerSection +public net.minecraft.server.level.ChunkHolder hasChangedSections diff --git a/worldedit-neoforge/src/main/resources/worldedit-neoforge.mixins.json b/worldedit-neoforge/src/main/resources/worldedit-neoforge.mixins.json index b807508229..c944fc56ca 100644 --- a/worldedit-neoforge/src/main/resources/worldedit-neoforge.mixins.json +++ b/worldedit-neoforge/src/main/resources/worldedit-neoforge.mixins.json @@ -5,10 +5,13 @@ "mixins": [ "AccessorServerPlayerGameMode", "MixinLevelChunkSetBlockHook", + "MixinNativeBlockState", + "MixinNativeChunk", + "MixinNativeChunkSection", + "MixinNativePosition", + "MixinNativeWorld", "MixinServerGamePacketListenerImpl" ], - "server": [ - ], "injectors": { "defaultRequire": 1 }, diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorld.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorld.java index 5f308f7dce..ccb7c62f3a 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorld.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorld.java @@ -32,7 +32,6 @@ import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.sponge.internal.NbtAdapter; -import com.sk89q.worldedit.sponge.internal.SpongeWorldNativeAccess; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffectSet; @@ -103,7 +102,6 @@ public final class SpongeWorld extends AbstractWorld { private static final Logger LOGGER = LogManagerCompat.getLogger(); private final WeakReference worldRef; - private final SpongeWorldNativeAccess worldNativeAccess; /** * Construct a new world. @@ -113,7 +111,6 @@ public final class SpongeWorld extends AbstractWorld { SpongeWorld(ServerWorld world) { checkNotNull(world); this.worldRef = new WeakReference<>(world); - this.worldNativeAccess = new SpongeWorldNativeAccess(new WeakReference<>((ServerLevel) world)); } /** @@ -232,8 +229,6 @@ public > boolean setBlock(BlockVector3 position, B public Set applySideEffects(BlockVector3 position, com.sk89q.worldedit.world.block.BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException { checkNotNull(position); - worldNativeAccess.applySideEffects(position, previousType, sideEffectSet); - return Sets.intersection( SpongeWorldEdit.inst().getInternalPlatform().getSupportedSideEffects(), sideEffectSet.getSideEffectsToApply() diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/ConfigurateConfiguration.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/ConfigurateConfiguration.java index 14adfe8e0b..a2aae511b3 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/ConfigurateConfiguration.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/ConfigurateConfiguration.java @@ -145,5 +145,7 @@ public void load() { setDefaultLocaleName(node.node("default-locale").getString(defaultLocaleName)); commandBlockSupport = node.node("command-block-support").getBoolean(false); + + chunkSectionEditing = node.node("chunk-section-editing").getBoolean(chunkSectionEditing); } } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/SpongeWorldNativeAccess.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/SpongeWorldNativeAccess.java deleted file mode 100644 index f831404759..0000000000 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/SpongeWorldNativeAccess.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.sponge.internal; - -import com.sk89q.worldedit.internal.wna.WorldNativeAccess; -import com.sk89q.worldedit.sponge.SpongeAdapter; -import com.sk89q.worldedit.util.SideEffect; -import com.sk89q.worldedit.util.SideEffectSet; -import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.level.FullChunkStatus; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.LevelChunk; -import org.enginehub.linbus.tree.LinCompoundTag; - -import java.lang.ref.WeakReference; -import java.util.Objects; -import javax.annotation.Nullable; - -public class SpongeWorldNativeAccess implements WorldNativeAccess { - private static final int UPDATE = 1; - private static final int NOTIFY = 2; - - private final WeakReference world; - private SideEffectSet sideEffectSet; - - public SpongeWorldNativeAccess(WeakReference world) { - this.world = world; - } - - private ServerLevel getWorld() { - return Objects.requireNonNull(world.get(), "The reference to the world was lost"); - } - - @Override - public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { - this.sideEffectSet = sideEffectSet; - } - - @Override - public LevelChunk getChunk(int x, int z) { - return getWorld().getChunk(x, z); - } - - @Override - public BlockState toNative(com.sk89q.worldedit.world.block.BlockState state) { - return (BlockState) SpongeAdapter.adapt(state); - } - - @Override - public BlockState getBlockState(LevelChunk chunk, BlockPos position) { - return chunk.getBlockState(position); - } - - @Nullable - @Override - public BlockState setBlockState(LevelChunk chunk, BlockPos position, BlockState state) { - if (chunk instanceof ExtendedChunk) { - return ((ExtendedChunk) chunk).setBlockState( - position, state, false, sideEffectSet.shouldApply(SideEffect.UPDATE) - ); - } - return chunk.setBlockState(position, state, false); - } - - @Override - public BlockState getValidBlockForPosition(BlockState block, BlockPos position) { - return Block.updateFromNeighbourShapes(block, getWorld(), position); - } - - @Override - public BlockPos getPosition(int x, int y, int z) { - return new BlockPos(x, y, z); - } - - @Override - public void updateLightingForBlock(BlockPos position) { - getWorld().getChunkSource().getLightEngine().checkBlock(position); - } - - @Override - public boolean updateTileEntity(BlockPos position, LinCompoundTag tag) { - CompoundTag nativeTag = NbtAdapter.adaptNMSToWorldEdit(tag); - BlockEntity tileEntity = getWorld().getChunk(position).getBlockEntity(position); - if (tileEntity == null) { - return false; - } - tileEntity.setLevel(getWorld()); - tileEntity.loadWithComponents(nativeTag, getWorld().registryAccess()); - return true; - } - - @Override - public void notifyBlockUpdate(LevelChunk chunk, BlockPos position, BlockState oldState, BlockState newState) { - if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { - getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY); - } - } - - @Override - public boolean isChunkTicking(LevelChunk chunk) { - return chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING); - } - - @Override - public void markBlockChanged(LevelChunk chunk, BlockPos position) { - if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { - getWorld().getChunkSource().blockChanged(position); - } - } - - @Override - public void notifyNeighbors(BlockPos pos, BlockState oldState, BlockState newState) { - getWorld().updateNeighborsAt(pos, oldState.getBlock()); - if (newState.hasAnalogOutputSignal()) { - getWorld().updateNeighbourForOutputSignal(pos, newState.getBlock()); - } - } - - @Override - public void updateBlock(BlockPos pos, BlockState oldState, BlockState newState) { - ServerLevel world = getWorld(); - newState.onPlace(world, pos, oldState, false); - } - - @Override - public void updateNeighbors(BlockPos pos, BlockState oldState, BlockState newState, int recursionLimit) { - ServerLevel world = getWorld(); - oldState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit); - newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); - newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit); - } - - @Override - public void onBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) { - getWorld().onBlockStateChange(pos, oldState, newState); - } -} From cf065ee0d4e10667daef5f54bdadbf0c3b922541 Mon Sep 17 00:00:00 2001 From: Octavia Togami Date: Sun, 9 Mar 2025 12:10:28 -0700 Subject: [PATCH 2/4] Rename ChunkSectionMask and clean it up --- .../v1_21_3/wna/PaperweightNativeChunk.java | 10 ++--- .../v1_21_4/wna/PaperweightNativeChunk.java | 10 ++--- .../internal/SectionBufferingExtent.java | 8 ++-- ...ctionMask.java => ChunkSectionPosSet.java} | 45 +++++++------------ .../worldedit/internal/wna/NativeChunk.java | 10 ++--- .../worldedit/internal/wna/WNASharedImpl.java | 6 +-- .../fabric/mixin/MixinNativeChunk.java | 10 ++--- .../neoforge/mixin/MixinNativeChunk.java | 10 ++--- 8 files changed, 48 insertions(+), 61 deletions(-) rename worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/collection/{ChunkSectionMask.java => ChunkSectionPosSet.java} (71%) diff --git a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunk.java b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunk.java index 0192e4b3aa..df9ac650d2 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunk.java +++ b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunk.java @@ -23,7 +23,7 @@ import com.google.common.base.Throwables; import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_3.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_3.StaticRefraction; -import com.sk89q.worldedit.internal.util.collection.ChunkSectionMask; +import com.sk89q.worldedit.internal.util.collection.ChunkSectionPosSet; import com.sk89q.worldedit.internal.wna.NativeBlockState; import com.sk89q.worldedit.internal.wna.NativeChunk; import com.sk89q.worldedit.internal.wna.NativeChunkSection; @@ -125,7 +125,7 @@ public NativeBlockState getBlockState(NativePosition blockPos) { } @Override - public void markSectionChanged(int index, ChunkSectionMask changed) { + public void markSectionChanged(int index, ChunkSectionPosSet changed) { ServerChunkCache serverChunkCache = (ServerChunkCache) delegate.getLevel().getChunkSource(); ChunkHolder holder; try { @@ -152,9 +152,9 @@ public void markSectionChanged(int index, ChunkSectionMask changed) { Throwables.throwIfUnchecked(e); throw new RuntimeException(e); } - changedBlocksPerSection[index] = new ShortOpenHashSet(changed.asShortCollection()); + changedBlocksPerSection[index] = new ShortOpenHashSet(changed.asSectionPosEncodedShorts()); } else { - changedBlocksPerSection[index].addAll(changed.asShortCollection()); + changedBlocksPerSection[index].addAll(changed.asSectionPosEncodedShorts()); } // Trick to get the holder into the broadcast set serverChunkCache.onChunkReadyToSend(holder); @@ -207,7 +207,7 @@ public NativeChunkSection getChunkSection(int index) { } @Override - public NativeChunkSection setChunkSection(int index, NativeChunkSection section, ChunkSectionMask modifiedBlocks) { + public NativeChunkSection setChunkSection(int index, NativeChunkSection section, ChunkSectionPosSet modifiedBlocks) { Preconditions.checkPositionIndex(index, delegate.getSectionsCount()); LevelChunkSection[] chunkSections = delegate.getSections(); var oldSection = new PaperweightNativeChunkSection(chunkSections[index]); diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunk.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunk.java index afa86c4bb5..457c76d269 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunk.java +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunk.java @@ -23,7 +23,7 @@ import com.google.common.base.Throwables; import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_4.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.v1_21_4.StaticRefraction; -import com.sk89q.worldedit.internal.util.collection.ChunkSectionMask; +import com.sk89q.worldedit.internal.util.collection.ChunkSectionPosSet; import com.sk89q.worldedit.internal.wna.NativeBlockState; import com.sk89q.worldedit.internal.wna.NativeChunk; import com.sk89q.worldedit.internal.wna.NativeChunkSection; @@ -125,7 +125,7 @@ public NativeBlockState getBlockState(NativePosition blockPos) { } @Override - public void markSectionChanged(int index, ChunkSectionMask changed) { + public void markSectionChanged(int index, ChunkSectionPosSet changed) { ServerChunkCache serverChunkCache = (ServerChunkCache) delegate.getLevel().getChunkSource(); ChunkHolder holder; try { @@ -152,9 +152,9 @@ public void markSectionChanged(int index, ChunkSectionMask changed) { Throwables.throwIfUnchecked(e); throw new RuntimeException(e); } - changedBlocksPerSection[index] = new ShortOpenHashSet(changed.asShortCollection()); + changedBlocksPerSection[index] = new ShortOpenHashSet(changed.asSectionPosEncodedShorts()); } else { - changedBlocksPerSection[index].addAll(changed.asShortCollection()); + changedBlocksPerSection[index].addAll(changed.asSectionPosEncodedShorts()); } // Trick to get the holder into the broadcast set serverChunkCache.onChunkReadyToSend(holder); @@ -207,7 +207,7 @@ public NativeChunkSection getChunkSection(int index) { } @Override - public NativeChunkSection setChunkSection(int index, NativeChunkSection section, ChunkSectionMask modifiedBlocks) { + public NativeChunkSection setChunkSection(int index, NativeChunkSection section, ChunkSectionPosSet modifiedBlocks) { Preconditions.checkPositionIndex(index, delegate.getSectionsCount()); LevelChunkSection[] chunkSections = delegate.getSections(); var oldSection = new PaperweightNativeChunkSection(chunkSections[index]); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/internal/SectionBufferingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/internal/SectionBufferingExtent.java index 0e794c7b96..87c7bb2f0a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/internal/SectionBufferingExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/internal/SectionBufferingExtent.java @@ -27,7 +27,7 @@ import com.sk89q.worldedit.extent.world.SideEffectExtent; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.RunContext; -import com.sk89q.worldedit.internal.util.collection.ChunkSectionMask; +import com.sk89q.worldedit.internal.util.collection.ChunkSectionPosSet; import com.sk89q.worldedit.internal.wna.NativeBlockState; import com.sk89q.worldedit.internal.wna.NativeChunk; import com.sk89q.worldedit.internal.wna.NativeChunkSection; @@ -60,7 +60,7 @@ private static long getTopSectionKey(BlockVector3 sectionPos) { private record SectionData( NativeChunkSection section, - ChunkSectionMask modified + ChunkSectionPosSet modified ) { } @@ -139,7 +139,7 @@ public > boolean setBlock(BlockVector3 location, B } data = new SectionData( nativeWorld.getChunk(sectionPos.x(), sectionPos.z()).getChunkSection(sectionPos.y()).copy(), - new ChunkSectionMask() + new ChunkSectionPosSet() ); sectionTable.computeIfAbsent(getTopSectionKey(sectionPos), k -> new Int2ObjectArrayMap<>()) .put(sectionPos.y(), data); @@ -196,7 +196,7 @@ private void finishOperation() { record SectionDataWithOld( NativeChunkSection section, NativeChunkSection oldSection, - ChunkSectionMask modified + ChunkSectionPosSet modified ) { } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/collection/ChunkSectionMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/collection/ChunkSectionPosSet.java similarity index 71% rename from worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/collection/ChunkSectionMask.java rename to worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/collection/ChunkSectionPosSet.java index 2857fcf5d4..70e67667de 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/collection/ChunkSectionMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/collection/ChunkSectionPosSet.java @@ -26,12 +26,17 @@ import java.util.BitSet; /** - * A mask for a chunk section. + * A set of positions in a chunk section. + * + *

+ * This has a defined order based on the order of the bits in the internal encoding. It is not guaranteed to be stable + * between Minecraft versions, but it is stable within a single one. + *

*/ -public class ChunkSectionMask { - public static int index(int x, int y, int z) { +public final class ChunkSectionPosSet { + private static int index(int x, int y, int z) { // Each value is 0-15, so 4 bits - // NOTE: This order specifically matches the order used by SectionPos in Minecraft, do not change unless they do + // NOTE: This encoding specifically matches the encoding used by SectionPos in Minecraft, do not change unless they do return (x << 8) | (z << 4) | y; } @@ -42,57 +47,39 @@ public interface PosConsumer { private final BitSet mask = new BitSet(4096); - public boolean isSet(int x, int y, int z) { - return mask.get(index(x, y, z)); - } - public void set(int x, int y, int z) { mask.set(index(x, y, z)); } - public void clear(int x, int y, int z) { - mask.clear(index(x, y, z)); - } - - public void clear() { - mask.clear(); - } - - public void setAll() { - mask.set(0, 4096); - } - public void forEach(PosConsumer consumer) { for (int i = mask.nextSetBit(0); i >= 0; i = mask.nextSetBit(i + 1)) { consumer.apply((i >> 8) & 0xF, i & 0xF, (i >> 4) & 0xF); } } - public int cardinality() { - return mask.cardinality(); - } - /** - * {@return a view of this mask as a short collection} Used for updating MC internals. + * {@return a view of this set as a short collection} These shorts match those used by {@code SectionPos}. */ - public ShortCollection asShortCollection() { + public ShortCollection asSectionPosEncodedShorts() { return new AbstractShortCollection() { @Override public ShortIterator iterator() { return new ShortIterator() { private int next = mask.nextSetBit(0); - @Override public short nextShort() { + @Override + public short nextShort() { if (!hasNext()) { throw new IllegalStateException(); } - // Uses the fact that we share the order with SectionPos to efficiently map + // Uses the fact that we share the encoding with SectionPos to efficiently map short value = (short) next; next = mask.nextSetBit(next + 1); return value; } - @Override public boolean hasNext() { + @Override + public boolean hasNext() { return next >= 0; } }; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeChunk.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeChunk.java index 64ac436bd9..8af904f0d6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeChunk.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeChunk.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.internal.wna; -import com.sk89q.worldedit.internal.util.collection.ChunkSectionMask; +import com.sk89q.worldedit.internal.util.collection.ChunkSectionPosSet; import javax.annotation.Nullable; @@ -38,7 +38,7 @@ public interface NativeChunk { @Nullable NativeBlockState setBlockState(NativePosition blockPos, NativeBlockState newState, boolean update); - void markSectionChanged(int index, ChunkSectionMask changed); + void markSectionChanged(int index, ChunkSectionPosSet changed); void updateHeightmaps(); @@ -60,12 +60,12 @@ public interface NativeChunk { * Replaces a chunk section in the given chunk. This method is also responsible for updating heightmaps * and creating block entities, to keep consistency with {@link #setBlockState(NativePosition, NativeBlockState, boolean)} * (the method we used to use). This is usually easily done by calling - * {@link WNASharedImpl#postChunkSectionReplacement(NativeChunk, int, NativeChunkSection, NativeChunkSection, ChunkSectionMask)}. + * {@link WNASharedImpl#postChunkSectionReplacement(NativeChunk, int, NativeChunkSection, NativeChunkSection, ChunkSectionPosSet)}. * * @param index the index, from 0 to the max height divided by 16 * @param section the new chunk section - * @param modifiedBlocks the mask of modified blocks + * @param modifiedBlocks the set of modified blocks * @return the old chunk section */ - NativeChunkSection setChunkSection(int index, NativeChunkSection section, ChunkSectionMask modifiedBlocks); + NativeChunkSection setChunkSection(int index, NativeChunkSection section, ChunkSectionPosSet modifiedBlocks); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WNASharedImpl.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WNASharedImpl.java index 84167791f3..2abda4346f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WNASharedImpl.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WNASharedImpl.java @@ -20,7 +20,7 @@ package com.sk89q.worldedit.internal.wna; import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.internal.util.collection.ChunkSectionMask; +import com.sk89q.worldedit.internal.util.collection.ChunkSectionPosSet; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffectSet; @@ -167,11 +167,11 @@ public static void markAndNotifyBlock( * @param index the replaced section index * @param oldSection the old section * @param newSection the new section - * @param modifiedBlocks the mask of modified blocks + * @param modifiedBlocks the set of modified blocks */ public static void postChunkSectionReplacement( NativeChunk chunk, int index, NativeChunkSection oldSection, NativeChunkSection newSection, - ChunkSectionMask modifiedBlocks + ChunkSectionPosSet modifiedBlocks ) { modifiedBlocks.forEach((secX, secY, secZ) -> { NativeBlockState oldState = oldSection.getBlock(secX, secY, secZ); diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeChunk.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeChunk.java index b654b85173..8b3621b341 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeChunk.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeChunk.java @@ -21,7 +21,7 @@ import com.google.common.base.Preconditions; import com.sk89q.worldedit.fabric.internal.ExtendedChunk; -import com.sk89q.worldedit.internal.util.collection.ChunkSectionMask; +import com.sk89q.worldedit.internal.util.collection.ChunkSectionPosSet; import com.sk89q.worldedit.internal.wna.NativeBlockState; import com.sk89q.worldedit.internal.wna.NativeChunk; import com.sk89q.worldedit.internal.wna.NativeChunkSection; @@ -121,15 +121,15 @@ public MixinNativeChunk(ChunkPos chunkPos, UpgradeData upgradeData, LevelHeightA ); } - public void nc$markSectionChanged(int index, ChunkSectionMask changed) { + public void nc$markSectionChanged(int index, ChunkSectionPosSet changed) { ServerChunkCache serverChunkCache = (ServerChunkCache) getLevel().getChunkSource(); ChunkHolder holder = serverChunkCache.getVisibleChunkIfPresent(getPos().toLong()); if (holder != null) { if (holder.changedBlocksPerSection[index] == null) { holder.hasChangedSections = true; - holder.changedBlocksPerSection[index] = new ShortOpenHashSet(changed.asShortCollection()); + holder.changedBlocksPerSection[index] = new ShortOpenHashSet(changed.asSectionPosEncodedShorts()); } else { - holder.changedBlocksPerSection[index].addAll(changed.asShortCollection()); + holder.changedBlocksPerSection[index].addAll(changed.asSectionPosEncodedShorts()); } // Trick to get the holder into the broadcast set ((ServerChunkCache) getLevel().getChunkSource()).onChunkReadyToSend(holder); @@ -171,7 +171,7 @@ public MixinNativeChunk(ChunkPos chunkPos, UpgradeData upgradeData, LevelHeightA return (NativeChunkSection) getSection(index); } - public NativeChunkSection nc$setChunkSection(int index, NativeChunkSection section, ChunkSectionMask modifiedBlocks) { + public NativeChunkSection nc$setChunkSection(int index, NativeChunkSection section, ChunkSectionPosSet modifiedBlocks) { Preconditions.checkPositionIndex(index, getSectionsCount()); LevelChunkSection[] chunkSections = getSections(); var oldSection = (NativeChunkSection) chunkSections[index]; diff --git a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeChunk.java b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeChunk.java index a672da4237..3b6e115506 100644 --- a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeChunk.java +++ b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeChunk.java @@ -20,7 +20,7 @@ package com.sk89q.worldedit.neoforge.mixin; import com.google.common.base.Preconditions; -import com.sk89q.worldedit.internal.util.collection.ChunkSectionMask; +import com.sk89q.worldedit.internal.util.collection.ChunkSectionPosSet; import com.sk89q.worldedit.internal.wna.NativeBlockState; import com.sk89q.worldedit.internal.wna.NativeChunk; import com.sk89q.worldedit.internal.wna.NativeChunkSection; @@ -121,15 +121,15 @@ public MixinNativeChunk(ChunkPos chunkPos, UpgradeData upgradeData, LevelHeightA ); } - public void nc$markSectionChanged(int index, ChunkSectionMask changed) { + public void nc$markSectionChanged(int index, ChunkSectionPosSet changed) { ServerChunkCache serverChunkCache = (ServerChunkCache) getLevel().getChunkSource(); ChunkHolder holder = serverChunkCache.getVisibleChunkIfPresent(getPos().toLong()); if (holder != null) { if (holder.changedBlocksPerSection[index] == null) { holder.hasChangedSections = true; - holder.changedBlocksPerSection[index] = new ShortOpenHashSet(changed.asShortCollection()); + holder.changedBlocksPerSection[index] = new ShortOpenHashSet(changed.asSectionPosEncodedShorts()); } else { - holder.changedBlocksPerSection[index].addAll(changed.asShortCollection()); + holder.changedBlocksPerSection[index].addAll(changed.asSectionPosEncodedShorts()); } // Trick to get the holder into the broadcast set ((ServerChunkCache) getLevel().getChunkSource()).onChunkReadyToSend(holder); @@ -171,7 +171,7 @@ public MixinNativeChunk(ChunkPos chunkPos, UpgradeData upgradeData, LevelHeightA return (NativeChunkSection) getSection(index); } - public NativeChunkSection nc$setChunkSection(int index, NativeChunkSection section, ChunkSectionMask modifiedBlocks) { + public NativeChunkSection nc$setChunkSection(int index, NativeChunkSection section, ChunkSectionPosSet modifiedBlocks) { Preconditions.checkPositionIndex(index, getSectionsCount()); LevelChunkSection[] chunkSections = getSections(); var oldSection = (NativeChunkSection) chunkSections[index]; From 6c96fc7eccb8b1ef04d6732a097ec5c1810e6d68 Mon Sep 17 00:00:00 2001 From: Octavia Togami Date: Sun, 9 Mar 2025 12:27:11 -0700 Subject: [PATCH 3/4] Remove NativeAdapter impls from API surface --- .../impl/v1_21_3/PaperweightAdapter.java | 8 +-- .../wna/PaperweightNativeBlockState.java | 10 +++- .../v1_21_3/wna/PaperweightNativeChunk.java | 24 ++++++--- .../wna/PaperweightNativeChunkSection.java | 10 +++- .../v1_21_3/wna/PaperweightNativeWorld.java | 37 +++++++++----- .../impl/v1_21_4/PaperweightAdapter.java | 8 +-- .../wna/PaperweightNativeBlockState.java | 10 +++- .../v1_21_4/wna/PaperweightNativeChunk.java | 24 ++++++--- .../wna/PaperweightNativeChunkSection.java | 10 +++- .../v1_21_4/wna/PaperweightNativeWorld.java | 37 +++++++++----- .../sk89q/worldedit/fabric/FabricAdapter.java | 25 +-------- .../fabric/internal/FabricNativeAdapter.java | 50 ++++++++++++++++++ .../fabric/mixin/MixinNativeWorld.java | 4 +- .../worldedit/neoforge/NeoForgeAdapter.java | 25 +-------- .../internal/NeoForgeNativeAdapter.java | 51 +++++++++++++++++++ .../neoforge/mixin/MixinNativeWorld.java | 4 +- 16 files changed, 227 insertions(+), 110 deletions(-) create mode 100644 worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricNativeAdapter.java create mode 100644 worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/internal/NeoForgeNativeAdapter.java diff --git a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/PaperweightAdapter.java index 13174e373d..0035ae1b37 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/PaperweightAdapter.java @@ -217,7 +217,7 @@ public NativeBlockState toNative(BlockState state) { @Override public BlockState fromNative(NativeBlockState state) { - return adapt(((PaperweightNativeBlockState) state).delegate()); + return adapt(((PaperweightNativeBlockState) state).delegate); } @Override public NativePosition newBlockPos(BlockVector3 pos) { @@ -282,10 +282,6 @@ public PaperweightAdapter() throws NoSuchFieldException, NoSuchMethodException { } } - public NativeAdapter asNativeAdapter() { - return nativeAdapter; - } - @Override public DataFixer getDataFixer() { return this.dataFixer; @@ -445,7 +441,7 @@ public void setBiome(Location location, BiomeType biome) { @Override public NativeWorld createNativeInterface(World world) { - return new PaperweightNativeWorld(this, ((CraftWorld) world).getHandle()); + return new PaperweightNativeWorld(this, nativeAdapter, ((CraftWorld) world).getHandle()); } private static net.minecraft.core.Direction adapt(Direction face) { diff --git a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeBlockState.java b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeBlockState.java index 08f85c476e..0b7760d1db 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeBlockState.java +++ b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeBlockState.java @@ -26,7 +26,13 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; -public record PaperweightNativeBlockState(BlockState delegate) implements NativeBlockState { +public final class PaperweightNativeBlockState implements NativeBlockState { + public final BlockState delegate; + + public PaperweightNativeBlockState(BlockState delegate) { + this.delegate = delegate; + } + @Override public boolean isSame(NativeBlockState other) { return this.delegate == ((PaperweightNativeBlockState) other).delegate; @@ -45,7 +51,7 @@ public boolean hasBlockEntity() { @Override public NativeBlockState updateFromNeighbourShapes(NativeWorld world, NativePosition position) { return new PaperweightNativeBlockState(Block.updateFromNeighbourShapes( - delegate, ((PaperweightNativeWorld) world).delegate(), PaperweightAdapter.adaptPos(position) + delegate, ((PaperweightNativeWorld) world).delegate, PaperweightAdapter.adaptPos(position) )); } } diff --git a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunk.java b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunk.java index df9ac650d2..dbfa01da6c 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunk.java +++ b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunk.java @@ -54,7 +54,7 @@ import java.util.Set; import javax.annotation.Nullable; -public record PaperweightNativeChunk(NativeWorld owner, LevelChunk delegate) implements NativeChunk { +public final class PaperweightNativeChunk implements NativeChunk { private static final MethodHandle GET_VISIBLE_CHUNK_IF_PRESENT; private static final MethodHandle GET_CHANGED_BLOCKS_PER_SECTION; private static final MethodHandle SET_HAS_CHANGED_SECTIONS; @@ -93,6 +93,14 @@ public record PaperweightNativeChunk(NativeWorld owner, LevelChunk delegate) imp Heightmap.Types.MOTION_BLOCKING_NO_LEAVES ); + private final NativeWorld owner; + private final LevelChunk delegate; + + public PaperweightNativeChunk(NativeWorld owner, LevelChunk delegate) { + this.owner = owner; + this.delegate = delegate; + } + @Override public NativeWorld getWorld() { return owner; @@ -116,12 +124,16 @@ public NativeBlockState getBlockState(NativePosition blockPos) { @Override public @Nullable NativeBlockState setBlockState(NativePosition blockPos, NativeBlockState newState, boolean update) { - return new PaperweightNativeBlockState(delegate.setBlockState( + BlockState blockState = delegate.setBlockState( PaperweightAdapter.adaptPos(blockPos), - ((PaperweightNativeBlockState) newState).delegate(), + ((PaperweightNativeBlockState) newState).delegate, false, update - )); + ); + if (blockState == null) { + return null; + } + return new PaperweightNativeBlockState(blockState); } @Override @@ -184,7 +196,7 @@ public void removeSectionBlockEntity(int chunkX, int chunkY, int chunkZ) { public void initializeBlockEntity(int chunkX, int chunkY, int chunkZ, NativeBlockState newState) { BlockPos pos = delegate.getPos().getBlockAt(chunkX, chunkY, chunkZ); BlockEntity blockEntity = delegate.getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK); - BlockState nativeState = ((PaperweightNativeBlockState) newState).delegate(); + BlockState nativeState = ((PaperweightNativeBlockState) newState).delegate; if (blockEntity == null) { blockEntity = ((EntityBlock) nativeState.getBlock()).newBlockEntity(pos, nativeState); if (blockEntity != null) { @@ -211,7 +223,7 @@ public NativeChunkSection setChunkSection(int index, NativeChunkSection section, Preconditions.checkPositionIndex(index, delegate.getSectionsCount()); LevelChunkSection[] chunkSections = delegate.getSections(); var oldSection = new PaperweightNativeChunkSection(chunkSections[index]); - chunkSections[index] = ((PaperweightNativeChunkSection) section).delegate(); + chunkSections[index] = ((PaperweightNativeChunkSection) section).delegate; WNASharedImpl.postChunkSectionReplacement(this, index, oldSection, section, modifiedBlocks); delegate.markUnsaved(); return oldSection; diff --git a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunkSection.java b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunkSection.java index 3e90d2463e..0317d9d084 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunkSection.java +++ b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeChunkSection.java @@ -24,7 +24,13 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunkSection; -public record PaperweightNativeChunkSection(LevelChunkSection delegate) implements NativeChunkSection { +public final class PaperweightNativeChunkSection implements NativeChunkSection { + final LevelChunkSection delegate; + + public PaperweightNativeChunkSection(LevelChunkSection delegate) { + this.delegate = delegate; + } + @Override public boolean isOnlyAir() { return delegate.hasOnlyAir(); @@ -32,7 +38,7 @@ public boolean isOnlyAir() { @Override public NativeBlockState getThenSetBlock(int i, int j, int k, NativeBlockState blockState) { - BlockState nativeState = ((PaperweightNativeBlockState) blockState).delegate(); + BlockState nativeState = ((PaperweightNativeBlockState) blockState).delegate; if (isOnlyAir() && nativeState.isAir()) { return blockState; } diff --git a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeWorld.java b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeWorld.java index fd71597f7c..35887399d4 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeWorld.java +++ b/worldedit-bukkit/adapters/adapter-1.21.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_3/wna/PaperweightNativeWorld.java @@ -39,10 +39,21 @@ import org.bukkit.event.block.BlockPhysicsEvent; import org.enginehub.linbus.tree.LinCompoundTag; -public record PaperweightNativeWorld(PaperweightAdapter adapter, ServerLevel delegate) implements NativeWorld { +public final class PaperweightNativeWorld implements NativeWorld { + + private final PaperweightAdapter adapter; + private final NativeAdapter nativeAdapter; + final ServerLevel delegate; + + public PaperweightNativeWorld(PaperweightAdapter adapter, NativeAdapter nativeAdapter, ServerLevel delegate) { + this.adapter = adapter; + this.nativeAdapter = nativeAdapter; + this.delegate = delegate; + } + @Override public NativeAdapter getAdapter() { - return adapter.asNativeAdapter(); + return nativeAdapter; } @Override @@ -64,8 +75,8 @@ public NativeChunk getChunk(int chunkX, int chunkZ) { public void notifyBlockUpdate(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { delegate.sendBlockUpdated( PaperweightAdapter.adaptPos(pos), - ((PaperweightNativeBlockState) oldState).delegate(), - ((PaperweightNativeBlockState) newState).delegate(), + ((PaperweightNativeBlockState) oldState).delegate, + ((PaperweightNativeBlockState) newState).delegate, Block.UPDATE_NEIGHBORS | Block.UPDATE_CLIENTS ); } @@ -104,10 +115,10 @@ public void notifyNeighbors( ) { BlockPos nativePos = PaperweightAdapter.adaptPos(pos); if (events) { - delegate.updateNeighborsAt(nativePos, ((PaperweightNativeBlockState) oldState).delegate().getBlock()); + delegate.updateNeighborsAt(nativePos, ((PaperweightNativeBlockState) oldState).delegate.getBlock()); } else { // When we don't want events, manually run the physics without them. - Block block = ((PaperweightNativeBlockState) oldState).delegate().getBlock(); + Block block = ((PaperweightNativeBlockState) oldState).delegate.getBlock(); fireNeighborChanged(nativePos, delegate, block, nativePos.west()); fireNeighborChanged(nativePos, delegate, block, nativePos.east()); fireNeighborChanged(nativePos, delegate, block, nativePos.below()); @@ -115,7 +126,7 @@ public void notifyNeighbors( fireNeighborChanged(nativePos, delegate, block, nativePos.north()); fireNeighborChanged(nativePos, delegate, block, nativePos.south()); } - BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate(); + BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate; if (nativeNewState.hasAnalogOutputSignal()) { delegate.updateNeighbourForOutputSignal(nativePos, nativeNewState.getBlock()); } @@ -128,8 +139,8 @@ private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, B @Override public void updateBlock(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { BlockPos nativePos = PaperweightAdapter.adaptPos(pos); - BlockState nativeOldState = ((PaperweightNativeBlockState) oldState).delegate(); - BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate(); + BlockState nativeOldState = ((PaperweightNativeBlockState) oldState).delegate; + BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate; nativeOldState.onRemove(delegate, nativePos, nativeNewState, false); nativeNewState.onPlace(delegate, nativePos, nativeOldState, false); } @@ -139,8 +150,8 @@ public void updateNeighbors( NativePosition pos, NativeBlockState oldState, NativeBlockState newState, int recursionLimit, boolean events ) { BlockPos nativePos = PaperweightAdapter.adaptPos(pos); - BlockState nativeOldState = ((PaperweightNativeBlockState) oldState).delegate(); - BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate(); + BlockState nativeOldState = ((PaperweightNativeBlockState) oldState).delegate; + BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate; nativeOldState.updateIndirectNeighbourShapes(delegate, nativePos, Block.UPDATE_CLIENTS, recursionLimit); if (events) { BlockPhysicsEvent event = new BlockPhysicsEvent( @@ -160,8 +171,8 @@ public void updateNeighbors( public void onBlockStateChange(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { delegate.onBlockStateChange( PaperweightAdapter.adaptPos(pos), - ((PaperweightNativeBlockState) oldState).delegate(), - ((PaperweightNativeBlockState) newState).delegate() + ((PaperweightNativeBlockState) oldState).delegate, + ((PaperweightNativeBlockState) newState).delegate ); } } diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java index c86bad6f76..e2c58a2ad4 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java @@ -217,7 +217,7 @@ public NativeBlockState toNative(BlockState state) { @Override public BlockState fromNative(NativeBlockState state) { - return adapt(((PaperweightNativeBlockState) state).delegate()); + return adapt(((PaperweightNativeBlockState) state).delegate); } @Override public NativePosition newBlockPos(BlockVector3 pos) { @@ -282,10 +282,6 @@ public PaperweightAdapter() throws NoSuchFieldException, NoSuchMethodException { } } - public NativeAdapter asNativeAdapter() { - return nativeAdapter; - } - @Override public DataFixer getDataFixer() { return this.dataFixer; @@ -445,7 +441,7 @@ public void setBiome(Location location, BiomeType biome) { @Override public NativeWorld createNativeInterface(World world) { - return new PaperweightNativeWorld(this, ((CraftWorld) world).getHandle()); + return new PaperweightNativeWorld(this, nativeAdapter, ((CraftWorld) world).getHandle()); } private static net.minecraft.core.Direction adapt(Direction face) { diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeBlockState.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeBlockState.java index 973fc9f93b..bab61e9313 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeBlockState.java +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeBlockState.java @@ -26,7 +26,13 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; -public record PaperweightNativeBlockState(BlockState delegate) implements NativeBlockState { +public final class PaperweightNativeBlockState implements NativeBlockState { + public final BlockState delegate; + + public PaperweightNativeBlockState(BlockState delegate) { + this.delegate = delegate; + } + @Override public boolean isSame(NativeBlockState other) { return this.delegate == ((PaperweightNativeBlockState) other).delegate; @@ -45,7 +51,7 @@ public boolean hasBlockEntity() { @Override public NativeBlockState updateFromNeighbourShapes(NativeWorld world, NativePosition position) { return new PaperweightNativeBlockState(Block.updateFromNeighbourShapes( - delegate, ((PaperweightNativeWorld) world).delegate(), PaperweightAdapter.adaptPos(position) + delegate, ((PaperweightNativeWorld) world).delegate, PaperweightAdapter.adaptPos(position) )); } } diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunk.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunk.java index 457c76d269..556044ac2b 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunk.java +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunk.java @@ -54,7 +54,7 @@ import java.util.Set; import javax.annotation.Nullable; -public record PaperweightNativeChunk(NativeWorld owner, LevelChunk delegate) implements NativeChunk { +public final class PaperweightNativeChunk implements NativeChunk { private static final MethodHandle GET_VISIBLE_CHUNK_IF_PRESENT; private static final MethodHandle GET_CHANGED_BLOCKS_PER_SECTION; private static final MethodHandle SET_HAS_CHANGED_SECTIONS; @@ -93,6 +93,14 @@ public record PaperweightNativeChunk(NativeWorld owner, LevelChunk delegate) imp Heightmap.Types.MOTION_BLOCKING_NO_LEAVES ); + private final NativeWorld owner; + private final LevelChunk delegate; + + public PaperweightNativeChunk(NativeWorld owner, LevelChunk delegate) { + this.owner = owner; + this.delegate = delegate; + } + @Override public NativeWorld getWorld() { return owner; @@ -116,12 +124,16 @@ public NativeBlockState getBlockState(NativePosition blockPos) { @Override public @Nullable NativeBlockState setBlockState(NativePosition blockPos, NativeBlockState newState, boolean update) { - return new PaperweightNativeBlockState(delegate.setBlockState( + BlockState blockState = delegate.setBlockState( PaperweightAdapter.adaptPos(blockPos), - ((PaperweightNativeBlockState) newState).delegate(), + ((PaperweightNativeBlockState) newState).delegate, false, update - )); + ); + if (blockState == null) { + return null; + } + return new PaperweightNativeBlockState(blockState); } @Override @@ -184,7 +196,7 @@ public void removeSectionBlockEntity(int chunkX, int chunkY, int chunkZ) { public void initializeBlockEntity(int chunkX, int chunkY, int chunkZ, NativeBlockState newState) { BlockPos pos = delegate.getPos().getBlockAt(chunkX, chunkY, chunkZ); BlockEntity blockEntity = delegate.getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK); - BlockState nativeState = ((PaperweightNativeBlockState) newState).delegate(); + BlockState nativeState = ((PaperweightNativeBlockState) newState).delegate; if (blockEntity == null) { blockEntity = ((EntityBlock) nativeState.getBlock()).newBlockEntity(pos, nativeState); if (blockEntity != null) { @@ -211,7 +223,7 @@ public NativeChunkSection setChunkSection(int index, NativeChunkSection section, Preconditions.checkPositionIndex(index, delegate.getSectionsCount()); LevelChunkSection[] chunkSections = delegate.getSections(); var oldSection = new PaperweightNativeChunkSection(chunkSections[index]); - chunkSections[index] = ((PaperweightNativeChunkSection) section).delegate(); + chunkSections[index] = ((PaperweightNativeChunkSection) section).delegate; WNASharedImpl.postChunkSectionReplacement(this, index, oldSection, section, modifiedBlocks); delegate.markUnsaved(); return oldSection; diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunkSection.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunkSection.java index c5ff3b019d..75ba5ce282 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunkSection.java +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeChunkSection.java @@ -24,7 +24,13 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunkSection; -public record PaperweightNativeChunkSection(LevelChunkSection delegate) implements NativeChunkSection { +public final class PaperweightNativeChunkSection implements NativeChunkSection { + final LevelChunkSection delegate; + + public PaperweightNativeChunkSection(LevelChunkSection delegate) { + this.delegate = delegate; + } + @Override public boolean isOnlyAir() { return delegate.hasOnlyAir(); @@ -32,7 +38,7 @@ public boolean isOnlyAir() { @Override public NativeBlockState getThenSetBlock(int i, int j, int k, NativeBlockState blockState) { - BlockState nativeState = ((PaperweightNativeBlockState) blockState).delegate(); + BlockState nativeState = ((PaperweightNativeBlockState) blockState).delegate; if (isOnlyAir() && nativeState.isAir()) { return blockState; } diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeWorld.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeWorld.java index de32028e24..fc597b4439 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeWorld.java +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/wna/PaperweightNativeWorld.java @@ -39,10 +39,21 @@ import org.bukkit.event.block.BlockPhysicsEvent; import org.enginehub.linbus.tree.LinCompoundTag; -public record PaperweightNativeWorld(PaperweightAdapter adapter, ServerLevel delegate) implements NativeWorld { +public final class PaperweightNativeWorld implements NativeWorld { + + private final PaperweightAdapter adapter; + private final NativeAdapter nativeAdapter; + final ServerLevel delegate; + + public PaperweightNativeWorld(PaperweightAdapter adapter, NativeAdapter nativeAdapter, ServerLevel delegate) { + this.adapter = adapter; + this.nativeAdapter = nativeAdapter; + this.delegate = delegate; + } + @Override public NativeAdapter getAdapter() { - return adapter.asNativeAdapter(); + return nativeAdapter; } @Override @@ -64,8 +75,8 @@ public NativeChunk getChunk(int chunkX, int chunkZ) { public void notifyBlockUpdate(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { delegate.sendBlockUpdated( PaperweightAdapter.adaptPos(pos), - ((PaperweightNativeBlockState) oldState).delegate(), - ((PaperweightNativeBlockState) newState).delegate(), + ((PaperweightNativeBlockState) oldState).delegate, + ((PaperweightNativeBlockState) newState).delegate, Block.UPDATE_NEIGHBORS | Block.UPDATE_CLIENTS ); } @@ -104,10 +115,10 @@ public void notifyNeighbors( ) { BlockPos nativePos = PaperweightAdapter.adaptPos(pos); if (events) { - delegate.updateNeighborsAt(nativePos, ((PaperweightNativeBlockState) oldState).delegate().getBlock()); + delegate.updateNeighborsAt(nativePos, ((PaperweightNativeBlockState) oldState).delegate.getBlock()); } else { // When we don't want events, manually run the physics without them. - Block block = ((PaperweightNativeBlockState) oldState).delegate().getBlock(); + Block block = ((PaperweightNativeBlockState) oldState).delegate.getBlock(); fireNeighborChanged(nativePos, delegate, block, nativePos.west()); fireNeighborChanged(nativePos, delegate, block, nativePos.east()); fireNeighborChanged(nativePos, delegate, block, nativePos.below()); @@ -115,7 +126,7 @@ public void notifyNeighbors( fireNeighborChanged(nativePos, delegate, block, nativePos.north()); fireNeighborChanged(nativePos, delegate, block, nativePos.south()); } - BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate(); + BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate; if (nativeNewState.hasAnalogOutputSignal()) { delegate.updateNeighbourForOutputSignal(nativePos, nativeNewState.getBlock()); } @@ -128,8 +139,8 @@ private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, B @Override public void updateBlock(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { BlockPos nativePos = PaperweightAdapter.adaptPos(pos); - BlockState nativeOldState = ((PaperweightNativeBlockState) oldState).delegate(); - BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate(); + BlockState nativeOldState = ((PaperweightNativeBlockState) oldState).delegate; + BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate; nativeOldState.onRemove(delegate, nativePos, nativeNewState, false); nativeNewState.onPlace(delegate, nativePos, nativeOldState, false); } @@ -139,8 +150,8 @@ public void updateNeighbors( NativePosition pos, NativeBlockState oldState, NativeBlockState newState, int recursionLimit, boolean events ) { BlockPos nativePos = PaperweightAdapter.adaptPos(pos); - BlockState nativeOldState = ((PaperweightNativeBlockState) oldState).delegate(); - BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate(); + BlockState nativeOldState = ((PaperweightNativeBlockState) oldState).delegate; + BlockState nativeNewState = ((PaperweightNativeBlockState) newState).delegate; nativeOldState.updateIndirectNeighbourShapes(delegate, nativePos, Block.UPDATE_CLIENTS, recursionLimit); if (events) { BlockPhysicsEvent event = new BlockPhysicsEvent( @@ -160,8 +171,8 @@ public void updateNeighbors( public void onBlockStateChange(NativePosition pos, NativeBlockState oldState, NativeBlockState newState) { delegate.onBlockStateChange( PaperweightAdapter.adaptPos(pos), - ((PaperweightNativeBlockState) oldState).delegate(), - ((PaperweightNativeBlockState) newState).delegate() + ((PaperweightNativeBlockState) oldState).delegate, + ((PaperweightNativeBlockState) newState).delegate ); } } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java index 33ce0f3c8e..ecef7f062a 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java @@ -25,9 +25,6 @@ import com.sk89q.worldedit.fabric.internal.FabricTransmogrifier; import com.sk89q.worldedit.fabric.internal.NBTConverter; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; -import com.sk89q.worldedit.internal.wna.NativeAdapter; -import com.sk89q.worldedit.internal.wna.NativeBlockState; -import com.sk89q.worldedit.internal.wna.NativePosition; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.registry.state.Property; @@ -71,27 +68,6 @@ public final class FabricAdapter { - private static final NativeAdapter NATIVE_ADAPTER = new NativeAdapter() { - @Override - public NativeBlockState toNative(BlockState state) { - return (NativeBlockState) FabricAdapter.adapt(state); - } - - @Override - public BlockState fromNative(NativeBlockState state) { - return FabricAdapter.adapt((net.minecraft.world.level.block.state.BlockState) state); - } - - @Override - public NativePosition newBlockPos(BlockVector3 pos) { - return (NativePosition) new BlockPos(pos.x(), pos.y(), pos.z()); - } - }; - - public static NativeAdapter asNativeAdapter() { - return NATIVE_ADAPTER; - } - private FabricAdapter() { } @@ -309,4 +285,5 @@ public static Actor adaptCommandSource(CommandSourceStack commandSourceStack) { return new FabricCommandSender(commandSourceStack); } + } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricNativeAdapter.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricNativeAdapter.java new file mode 100644 index 0000000000..31466974f2 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricNativeAdapter.java @@ -0,0 +1,50 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric.internal; + +import com.sk89q.worldedit.fabric.FabricAdapter; +import com.sk89q.worldedit.internal.wna.NativeAdapter; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.block.BlockState; +import net.minecraft.core.BlockPos; + +public final class FabricNativeAdapter implements NativeAdapter { + public static final FabricNativeAdapter INSTANCE = new FabricNativeAdapter(); + + private FabricNativeAdapter() { + } + + @Override + public NativeBlockState toNative(BlockState state) { + return (NativeBlockState) FabricAdapter.adapt(state); + } + + @Override + public BlockState fromNative(NativeBlockState state) { + return FabricAdapter.adapt((net.minecraft.world.level.block.state.BlockState) state); + } + + @Override + public NativePosition newBlockPos(BlockVector3 pos) { + return (NativePosition) new BlockPos(pos.x(), pos.y(), pos.z()); + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeWorld.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeWorld.java index 3392e1a584..eeb33e3415 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeWorld.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinNativeWorld.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.fabric.mixin; -import com.sk89q.worldedit.fabric.FabricAdapter; +import com.sk89q.worldedit.fabric.internal.FabricNativeAdapter; import com.sk89q.worldedit.fabric.internal.NBTConverter; import com.sk89q.worldedit.internal.wna.NativeAdapter; import com.sk89q.worldedit.internal.wna.NativeBlockState; @@ -55,7 +55,7 @@ public MixinNativeWorld(WritableLevelData writableLevelData, ResourceKey } public NativeAdapter nw$getAdapter() { - return FabricAdapter.asNativeAdapter(); + return FabricNativeAdapter.INSTANCE; } public int nw$getSectionIndex(int y) { diff --git a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/NeoForgeAdapter.java b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/NeoForgeAdapter.java index 0d34953e19..3e60a74372 100644 --- a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/NeoForgeAdapter.java +++ b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/NeoForgeAdapter.java @@ -23,9 +23,6 @@ import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; -import com.sk89q.worldedit.internal.wna.NativeAdapter; -import com.sk89q.worldedit.internal.wna.NativeBlockState; -import com.sk89q.worldedit.internal.wna.NativePosition; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.neoforge.internal.NBTConverter; @@ -70,27 +67,6 @@ import static com.google.common.base.Preconditions.checkNotNull; public final class NeoForgeAdapter { - private static final NativeAdapter NATIVE_ADAPTER = new NativeAdapter() { - @Override - public NativeBlockState toNative(BlockState state) { - return (NativeBlockState) NeoForgeAdapter.adapt(state); - } - - @Override - public BlockState fromNative(NativeBlockState state) { - return NeoForgeAdapter.adapt((net.minecraft.world.level.block.state.BlockState) state); - } - - @Override - public NativePosition newBlockPos(BlockVector3 pos) { - return (NativePosition) new BlockPos(pos.x(), pos.y(), pos.z()); - } - }; - - public static NativeAdapter asNativeAdapter() { - return NATIVE_ADAPTER; - } - private NeoForgeAdapter() { } @@ -295,4 +271,5 @@ public static Actor adaptCommandSource(CommandSourceStack commandSourceStack) { return new NeoForgeCommandSender(commandSourceStack); } + } diff --git a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/internal/NeoForgeNativeAdapter.java b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/internal/NeoForgeNativeAdapter.java new file mode 100644 index 0000000000..86dfb97a3c --- /dev/null +++ b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/internal/NeoForgeNativeAdapter.java @@ -0,0 +1,51 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.neoforge.internal; + +import com.sk89q.worldedit.internal.wna.NativeAdapter; +import com.sk89q.worldedit.internal.wna.NativeBlockState; +import com.sk89q.worldedit.internal.wna.NativePosition; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.neoforge.NeoForgeAdapter; +import com.sk89q.worldedit.world.block.BlockState; +import net.minecraft.core.BlockPos; + +public class NeoForgeNativeAdapter implements NativeAdapter { + + public static final NeoForgeNativeAdapter INSTANCE = new NeoForgeNativeAdapter(); + + private NeoForgeNativeAdapter() { + } + + @Override + public NativeBlockState toNative(BlockState state) { + return (NativeBlockState) NeoForgeAdapter.adapt(state); + } + + @Override + public BlockState fromNative(NativeBlockState state) { + return NeoForgeAdapter.adapt((net.minecraft.world.level.block.state.BlockState) state); + } + + @Override + public NativePosition newBlockPos(BlockVector3 pos) { + return (NativePosition) new BlockPos(pos.x(), pos.y(), pos.z()); + } +} diff --git a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeWorld.java b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeWorld.java index b711388da0..b8e3a10b9a 100644 --- a/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeWorld.java +++ b/worldedit-neoforge/src/main/java/com/sk89q/worldedit/neoforge/mixin/MixinNativeWorld.java @@ -24,8 +24,8 @@ import com.sk89q.worldedit.internal.wna.NativeChunk; import com.sk89q.worldedit.internal.wna.NativePosition; import com.sk89q.worldedit.internal.wna.NativeWorld; -import com.sk89q.worldedit.neoforge.NeoForgeAdapter; import com.sk89q.worldedit.neoforge.internal.NBTConverter; +import com.sk89q.worldedit.neoforge.internal.NeoForgeNativeAdapter; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.RegistryAccess; @@ -55,7 +55,7 @@ public MixinNativeWorld(WritableLevelData writableLevelData, ResourceKey } public NativeAdapter nw$getAdapter() { - return NeoForgeAdapter.asNativeAdapter(); + return NeoForgeNativeAdapter.INSTANCE; } public int nw$getSectionIndex(int y) { From e27976dc889612f8d941ead50ff4467139ccdbd3 Mon Sep 17 00:00:00 2001 From: Octavia Togami Date: Sun, 9 Mar 2025 12:30:49 -0700 Subject: [PATCH 4/4] Clean up unnecessary changes to build logic --- build-logic/src/main/kotlin/buildlogic.adapter.gradle.kts | 1 - gradle.properties | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/build-logic/src/main/kotlin/buildlogic.adapter.gradle.kts b/build-logic/src/main/kotlin/buildlogic.adapter.gradle.kts index 14c8723bb7..ff66e21f49 100644 --- a/build-logic/src/main/kotlin/buildlogic.adapter.gradle.kts +++ b/build-logic/src/main/kotlin/buildlogic.adapter.gradle.kts @@ -26,7 +26,6 @@ repositories { dependencies { "implementation"(project(":worldedit-bukkit")) - "implementation"(stringyLibs.getLibrary("paperLib")) constraints { "remapper"("net.fabricmc:tiny-remapper:[${stringyLibs.getVersion("minimumTinyRemapper")},)") { because("Need remapper to support Java 21") diff --git a/gradle.properties b/gradle.properties index cde5cfcb0c..f3b8605355 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ group=com.sk89q.worldedit version=7.4.0-SNAPSHOT -org.gradle.jvmargs=-Xmx1700M +org.gradle.jvmargs=-Xmx1500M org.gradle.parallel=true loom_fabric_repository=https://maven.enginehub.org/artifactory/fabricmc/