+ * 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); + + /** + * Is this section 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..5c2721b775 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativePosition.java @@ -0,0 +1,12 @@ +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..6debad44dc --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/NativeWorld.java @@ -0,0 +1,53 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q+ * See Forge's World.markAndNotifyBlock + *
+ */ + public static void markAndNotifyBlock( + NativeWorld wna, NativePosition pos, NativeChunk chunk, NativeBlockState oldState, NativeBlockState newState, + SideEffectSet sideEffectSet + ) { + // Removed redundant branches + + if (chunk.isTicking()) { + if (sideEffectSet.shouldApply(SideEffect.ENTITY_AI)) { + chunk.notifyBlockUpdate(pos, oldState, newState); + } else if (sideEffectSet.shouldApply(SideEffect.NETWORK)) { + // If we want to skip entity AI, just mark the block for sending + chunk.markBlockChanged(pos); + } + } + + if (sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) { + wna.notifyNeighbors(pos, oldState, newState); + } + + // Make connection updates optional + if (sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) { + wna.updateNeighbors(pos, oldState, newState, 512); + } + + // Seems used only for PoI updates + if (sideEffectSet.shouldApply(SideEffect.POI_UPDATE)) { + wna.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)}. 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 + */ + public static void postChunkSectionReplacement( + NativeChunk chunk, int index, NativeChunkSection oldSection, NativeChunkSection newSection + ) { + for (int secX = 0; secX < 16; secX++) { + for (int secY = 0; secY < 16; secY++) { + for (int secZ = 0; secZ < 16; secZ++) { + NativeBlockState oldState = oldSection.getBlock(secX, secY, secZ); + NativeBlockState newState = newSection.getBlock(secX, secY, secZ); + if (oldState.isSame(newState)) { + continue; + } + 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- * 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/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