From 82adab77b4693289a95d934db79532065d2df679 Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Sun, 8 Mar 2020 16:09:36 +1000 Subject: [PATCH] Additional work towards 1.16 compatibility - Very basic implementation of the SideEffects system. Will definitely need fine tuning for it to be functional, but is not considered a priority in my opinion. - Minor changes to the World interface and World implementations related to the SideEffects system. Shouldn't be the cause of any new bugs but be on the lookout. - Included debug in BukkitImplLoader.java to assist contributors in understanding what needs to be implemented for the adapter to load properly. Still very WIP but we're a few steps closer. So far, this is coming along better than I anticipated. Hopefully we can keep the momentum. --- config/checkstyle/checkstyle.xml | 2 +- .../bukkit/BukkitServerInterface.java | 16 +++- .../sk89q/worldedit/bukkit/BukkitWorld.java | 32 ++++--- .../bukkit/adapter/BukkitImplAdapter.java | 20 +++- .../bukkit/adapter/BukkitImplLoader.java | 13 ++- .../adapter/IDelegateBukkitImplAdapter.java | 8 +- .../adapter/impl/FAWE_Spigot_v1_14_R4.java | 23 ++++- .../adapter/impl/FAWE_Spigot_v1_15_R2.java | 25 +++-- .../adapter/impl/FAWE_Spigot_v1_16_R1.java | 25 +++-- .../com/sk89q/worldedit/cli/CLIPlatform.java | 8 ++ .../cli/schematic/ClipboardWorld.java | 11 ++- .../com/boydti/fawe/jnbt/anvil/MCAWorld.java | 14 +++ .../cfi/HeightMapMCAGenerator.java | 22 +++-- .../boydti/fawe/wrappers/WorldWrapper.java | 16 +++- .../java/com/sk89q/worldedit/EditSession.java | 15 +++ .../com/sk89q/worldedit/LocalSession.java | 31 +++++- .../worldedit/command/GeneralCommands.java | 69 +++++++++++--- .../command/argument/EnumConverter.java | 7 ++ .../command/argument/SideEffectConverter.java | 75 +++++++++++++++ .../extension/platform/Platform.java | 4 + .../platform/PlatformCommandManager.java | 2 + .../extension/platform/PlatformManager.java | 6 ++ .../worldedit/extent/buffer/ExtentBuffer.java | 5 +- .../extent/reorder/ChunkBatchingExtent.java | 1 - ...tModeExtent.java => SideEffectExtent.java} | 86 ++++++----------- .../com/sk89q/worldedit/util/SideEffect.java | 68 ++++++++++++++ .../sk89q/worldedit/util/SideEffectSet.java | 94 +++++++++++++++++++ .../formatting/component/SideEffectBox.java | 88 +++++++++++++++++ .../sk89q/worldedit/world/AbstractWorld.java | 3 +- .../com/sk89q/worldedit/world/NullWorld.java | 11 ++- .../java/com/sk89q/worldedit/world/World.java | 41 +++++++- .../src/main/resources/lang/strings.json | 20 ++++ worldedit-fabric/build.gradle.kts | 4 - .../worldedit/fabric/FabricPlatform.java | 15 ++- .../sk89q/worldedit/fabric/FabricWorld.java | 71 +++++++++++--- worldedit-forge/build.gradle.kts | 7 +- .../sk89q/worldedit/forge/ForgePlatform.java | 13 +++ .../com/sk89q/worldedit/forge/ForgeWorld.java | 73 ++++++++++++-- .../worldedit/sponge/SpongePlatform.java | 9 ++ 39 files changed, 877 insertions(+), 176 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/SideEffectConverter.java rename worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/{FastModeExtent.java => SideEffectExtent.java} (63%) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffect.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffectSet.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/SideEffectBox.java diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index ab1c6d499..8dbf07f8f 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -68,6 +68,6 @@ - + diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index d948d0e0a..99a4b183b 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.bukkit; +import com.google.common.collect.Sets; import com.sk89q.bukkit.util.CommandInfo; import com.sk89q.bukkit.util.CommandRegistration; import com.sk89q.worldedit.LocalConfiguration; @@ -30,6 +31,7 @@ import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.MultiUserPlatform; import com.sk89q.worldedit.extension.platform.Preference; import com.sk89q.worldedit.extension.platform.Watchdog; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.concurrency.LazyReference; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.registry.Registries; @@ -44,8 +46,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.EnumMap; import java.util.List; -import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -218,6 +220,18 @@ public class BukkitServerInterface implements MultiUserPlatform { return capabilities; } + private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( + SideEffect.NEIGHBORS + ); + + @Override + public Set getSupportedSideEffects() { + if (plugin.getBukkitImplAdapter() != null) { + return plugin.getBukkitImplAdapter().getSupportedSideEffects(); + } + return SUPPORTED_SIDE_EFFECTS; + } + public void unregisterCommands() { dynamicCommands.unregisterCommands(); } 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 2818a8434..f11fc13d3 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 @@ -25,6 +25,7 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.sk89q.jnbt.CompoundTag; +import com.google.common.collect.Sets; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; @@ -38,6 +39,8 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.biome.BiomeType; @@ -48,12 +51,7 @@ import com.sk89q.worldedit.world.weather.WeatherTypes; import io.papermc.lib.PaperLib; import java.lang.ref.WeakReference; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.util.*; import javax.annotation.Nullable; import org.bukkit.Effect; import org.bukkit.TreeType; @@ -466,11 +464,11 @@ public class BukkitWorld extends AbstractWorld { } @Override - public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException { + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); if (adapter != null) { try { - return adapter.setBlock(BukkitAdapter.adapt(getWorld(), position), block, notifyAndLight); + return adapter.setBlock(BukkitAdapter.adapt(getWorld(), position), block, sideEffects); } catch (Exception e) { if (block instanceof BaseBlock && ((BaseBlock) block).getNbtData() != null) { logger.warn("Tried to set a corrupt tile entity at " + position.toString()); @@ -478,12 +476,12 @@ public class BukkitWorld extends AbstractWorld { } e.printStackTrace(); Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()); - bukkitBlock.setBlockData(BukkitAdapter.adapt(block), notifyAndLight); + bukkitBlock.setBlockData(BukkitAdapter.adapt(block), sideEffects.doesApplyAny()); return true; } } else { Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()); - bukkitBlock.setBlockData(BukkitAdapter.adapt(block), false); + bukkitBlock.setBlockData(BukkitAdapter.adapt(block), sideEffects.doesApplyAny()); return true; } } @@ -499,14 +497,20 @@ public class BukkitWorld extends AbstractWorld { } @Override - public boolean notifyAndLightBlock(BlockVector3 position, com.sk89q.worldedit.world.block.BlockState previousType) throws WorldEditException { + public Set applySideEffects(BlockVector3 position, com.sk89q.worldedit.world.block.BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException { BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); if (adapter != null) { - adapter.notifyAndLightBlock(BukkitAdapter.adapt(getWorld(), position), previousType); - return true; + adapter.applySideEffects(BukkitAdapter.adapt(getWorld(), position), previousType, sideEffectSet); + return Sets.intersection( + adapter.getSupportedSideEffects(), + sideEffectSet.getSideEffectsToApply() + ); } - return false; + return Sets.intersection( + WorldEditPlugin.getInstance().getInternalPlatform().getSupportedSideEffects(), + sideEffectSet.getSideEffectsToApply() + ); } @Override 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 591626f0f..416b4b621 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 @@ -34,6 +34,8 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; @@ -43,6 +45,7 @@ import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.registry.BlockMaterial; import java.util.Map; import java.util.OptionalInt; +import java.util.Set; import javax.annotation.Nullable; import org.bukkit.Location; import org.bukkit.World; @@ -99,19 +102,19 @@ public interface BukkitImplAdapter extends IBukkitAdapter { * * @param location the location * @param state the block - * @param notifyAndLight notify and light if set + * @param sideEffectSet side effects to apply * @return true if a block was likely changed */ - > boolean setBlock(Location location, B state, boolean notifyAndLight); + boolean setBlock(Location location, BlockStateHolder state, SideEffectSet sideEffectSet); /** - * Notifies the simulation that the block at the given location has - * been changed and it must be re-lighted (and issue other events). + * Applies side effects on the given block. * * @param position position of the block * @param previousType the type of the previous block that was there + * @param sideEffectSet side effects to apply */ - void notifyAndLightBlock(Location position, BlockState previousType); + void applySideEffects(Location position, BlockState previousType, SideEffectSet sideEffectSet); /** * Get the state for the given entity. @@ -186,6 +189,13 @@ public interface BukkitImplAdapter extends IBukkitAdapter { */ BaseItemStack adapt(ItemStack itemStack); + /** + * Get the {@link SideEffect}s that this adapter supports. + * + * @return The side effects that are supported + */ + Set getSupportedSideEffects(); + default OptionalInt getInternalBlockStateId(BlockData data) { // return OptionalInt.empty(); return getInternalBlockStateId(BukkitAdapter.adapt(data)); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java index 172aaa2e7..00807b4b5 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java @@ -155,11 +155,20 @@ public class BukkitImplLoader { */ public BukkitImplAdapter loadAdapter() throws AdapterLoadException { for (String className : adapterCandidates) { + System.out.println("Candidate: " + className); try { Class cls = Class.forName(className); - if (cls.isSynthetic()) continue; + if (cls.isSynthetic()){ + System.out.println(className + " is synthetic, continuing"); + continue; + }else{ + System.out.println(className + " is not synthetic"); + } if (BukkitImplAdapter.class.isAssignableFrom(cls)) { + System.out.println(className + " is assignable from BukkitImplAdapter, returning"); return (BukkitImplAdapter) cls.newInstance(); + }else{ + System.out.println(className + " is NOT assignable from BukkitImplAdapter, returning"); } } catch (ClassNotFoundException e) { log.warn("Failed to load the Bukkit adapter class '" + className + @@ -170,6 +179,8 @@ public class BukkitImplLoader { } catch (Throwable e) { if (className.equals(customCandidate)) { log.warn("Failed to load the Bukkit adapter class '" + className + "'", e); + }else{ + log.warn(className + " is not custom candidate.", e); } } } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/IDelegateBukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/IDelegateBukkitImplAdapter.java index 6656c032f..9f5499dcd 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/IDelegateBukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/IDelegateBukkitImplAdapter.java @@ -12,6 +12,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; @@ -66,12 +67,7 @@ public interface IDelegateBukkitImplAdapter extends BukkitImplAdapter { } default > boolean setBlock(Location location, B state, boolean notifyAndLight) { - return getParent().setBlock(location, state, notifyAndLight); - } - - @Override - default void notifyAndLightBlock(Location position, BlockState previousType) { - getParent().notifyAndLightBlock(position, previousType); + return getParent().setBlock(location, state, SideEffectSet.none()); } @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_14_R4.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_14_R4.java index b5094f0db..21bcf1718 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_14_R4.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_14_R4.java @@ -45,6 +45,8 @@ import com.sk89q.worldedit.entity.LazyBaseEntity; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; @@ -71,6 +73,7 @@ import java.io.File; import java.io.IOException; import java.util.Map; import java.util.OptionalInt; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.function.Supplier; @@ -148,6 +151,21 @@ public final class FAWE_Spigot_v1_14_R4 extends CachedBukkitAdapter implements I return state.toBaseBlock(); } + @Override + public boolean setBlock(Location location, BlockStateHolder state, SideEffectSet sideEffectSet) { + return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, sideEffectSet.shouldApply(SideEffect.LIGHTING)); + } + + @Override + public void applySideEffects(Location position, BlockState previousType, SideEffectSet sideEffectSet) { + return; //TODO: properly implement SideEffects into FAWE + } + + @Override + public Set getSupportedSideEffects() { + return SideEffectSet.defaults().getSideEffectsToApply(); + } + @Override public > boolean setBlock(Location location, B state, boolean notifyAndLight) { return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, notifyAndLight); @@ -292,11 +310,6 @@ public final class FAWE_Spigot_v1_14_R4 extends CachedBukkitAdapter implements I return material.getCraftBlockData(); } - @Override - public void notifyAndLightBlock(Location position, BlockState previousType) { - this.setBlock(position.getChunk(), position.getBlockX(), position.getBlockY(), position.getBlockZ(), previousType, true); - } - private MapChunkUtil_1_14 mapUtil = new MapChunkUtil_1_14(); @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java index 3ee354bd6..ffbe28f1d 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java @@ -47,6 +47,8 @@ import com.sk89q.worldedit.entity.LazyBaseEntity; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; @@ -73,6 +75,7 @@ import java.io.File; import java.io.IOException; import java.util.Map; import java.util.OptionalInt; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -157,12 +160,27 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I return state.toBaseBlock(); } + @Override + public boolean setBlock(Location location, BlockStateHolder state, SideEffectSet sideEffectSet) { + return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, sideEffectSet.shouldApply(SideEffect.LIGHTING)); + } + + @Override + public void applySideEffects(Location position, BlockState previousType, SideEffectSet sideEffectSet) { + return; //TODO: properly implement SideEffects into FAWE + } + + @Override + public Set getSupportedSideEffects() { + return SideEffectSet.defaults().getSideEffectsToApply(); + } + @Override public > boolean setBlock(Location location, B state, boolean notifyAndLight) { return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, notifyAndLight); } - public > boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, B state, boolean update) { + public boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, BlockStateHolder state, boolean update) { CraftChunk craftChunk = (CraftChunk) chunk; Chunk nmsChunk = craftChunk.getHandle(); World nmsWorld = nmsChunk.getWorld(); @@ -301,11 +319,6 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I return material.getCraftBlockData(); } - @Override - public void notifyAndLightBlock(Location position, BlockState previousType) { - this.setBlock(position.getChunk(), position.getBlockX(), position.getBlockY(), position.getBlockZ(), previousType, true); - } - private MapChunkUtil_1_15_2 mapUtil = new MapChunkUtil_1_15_2(); @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java index 695aeca79..a97372095 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java @@ -47,6 +47,8 @@ import com.sk89q.worldedit.entity.LazyBaseEntity; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.*; @@ -72,6 +74,7 @@ import java.io.File; import java.io.IOException; import java.util.Map; import java.util.OptionalInt; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -156,12 +159,27 @@ public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements I return state.toBaseBlock(); } + @Override + public boolean setBlock(Location location, BlockStateHolder state, SideEffectSet sideEffectSet) { + return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, sideEffectSet.shouldApply(SideEffect.LIGHTING)); + } + + @Override + public void applySideEffects(Location position, BlockState previousType, SideEffectSet sideEffectSet) { + return; //TODO: properly implement SideEffects into FAWE + } + + @Override + public Set getSupportedSideEffects() { + return SideEffectSet.defaults().getSideEffectsToApply(); + } + @Override public > boolean setBlock(Location location, B state, boolean notifyAndLight) { return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, notifyAndLight); } - public > boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, B state, boolean update) { + public boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, BlockStateHolder state, boolean update) { CraftChunk craftChunk = (CraftChunk) chunk; Chunk nmsChunk = craftChunk.getHandle(); World nmsWorld = nmsChunk.getWorld(); @@ -300,11 +318,6 @@ public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements I return material.getCraftBlockData(); } - @Override - public void notifyAndLightBlock(Location position, BlockState previousType) { - this.setBlock(position.getChunk(), position.getBlockX(), position.getBlockY(), position.getBlockZ(), previousType, true); - } - private MapChunkUtil_1_16_1 mapUtil = new MapChunkUtil_1_16_1(); @Override diff --git a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIPlatform.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIPlatform.java index c2e9eaba9..7dcfa2daf 100644 --- a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIPlatform.java +++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIPlatform.java @@ -19,10 +19,12 @@ package com.sk89q.worldedit.cli; +import com.google.common.collect.ImmutableSet; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.AbstractPlatform; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Preference; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.entity.EntityTypes; @@ -33,6 +35,7 @@ import java.util.ArrayList; import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.Timer; import java.util.TimerTask; @@ -153,6 +156,11 @@ class CLIPlatform extends AbstractPlatform { return capabilities; } + @Override + public Set getSupportedSideEffects() { + return ImmutableSet.of(); + } + public void addWorld(World world) { worlds.add(world); } diff --git a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java index 69d1cb1c9..6be24d545 100644 --- a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java +++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.cli.schematic; +import com.google.common.collect.ImmutableSet; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; @@ -34,6 +35,8 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.biome.BiomeType; @@ -46,6 +49,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.List; import java.util.Locale; +import java.util.Set; import javax.annotation.Nullable; @@ -74,15 +78,14 @@ public class ClipboardWorld extends AbstractWorld implements Clipboard, CLIWorld } @Override - public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) - throws WorldEditException { + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { dirty = true; return clipboard.setBlock(position, block); } @Override - public boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException { - return false; + public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException { + return ImmutableSet.of(); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAWorld.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAWorld.java index a13cd75b6..6046c5928 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAWorld.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAWorld.java @@ -13,12 +13,16 @@ import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; import javax.annotation.Nullable; import java.io.File; +import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; @@ -35,6 +39,11 @@ public class MCAWorld extends AbstractWorld { return path.getName(); } + @Override + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { + return false; + } + @Override public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { @@ -46,6 +55,11 @@ public class MCAWorld extends AbstractWorld { return false; } + @Override + public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException { + return SideEffectSet.none().getSideEffectsToApply(); + } + @Override public boolean clearContainerBlockContents(BlockVector3 position) { return false; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java index 1e90346c4..b50bce169 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java @@ -29,10 +29,7 @@ import com.boydti.fawe.util.TextureUtil; import com.boydti.fawe.util.image.Drawable; import com.boydti.fawe.util.image.ImageViewer; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.MaxChangedBlocksException; -import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extent.clipboard.Clipboard; @@ -48,9 +45,7 @@ import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.session.ClipboardHolder; -import com.sk89q.worldedit.util.Identifiable; -import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.util.*; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; @@ -68,6 +63,7 @@ import java.lang.reflect.Field; import java.nio.file.Path; import java.util.Arrays; import java.util.List; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; import java.util.function.Supplier; @@ -750,12 +746,24 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr return BlockVector3.at(getWidth() - 1, 255, getLength() - 1); } + @Override + public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) + throws WorldEditException{ + return SideEffectSet.none().getSideEffectsToApply(); + } + @Override public > boolean setBlock(BlockVector3 position, B block) throws WorldEditException { return setBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ(), block); } + @Override + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) + throws WorldEditException { + return setBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ(), block); + } + private boolean setBlock(int x, int y, int z, char combined) { int index = z * getWidth() + x; if (index < 0 || index >= getArea()) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java b/worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java index 771262350..812b440dc 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java @@ -24,9 +24,7 @@ import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.util.Direction; -import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.util.*; import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; @@ -38,6 +36,7 @@ import com.sk89q.worldedit.world.weather.WeatherType; import javax.annotation.Nullable; import java.util.List; +import java.util.Set; public class WorldWrapper extends AbstractWorld { @@ -88,6 +87,12 @@ public class WorldWrapper extends AbstractWorld { return parent.useItem(position, item, face); } + @Override + public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) + throws WorldEditException{ + return parent.applySideEffects(position, previousType, sideEffectSet); + } + @Override public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException { return parent.setBlock(position, block, notifyAndLight); @@ -99,6 +104,11 @@ public class WorldWrapper extends AbstractWorld { return parent.setBlock(x, y, z, block); } + @Override + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { + return parent.setBlock(position, block, sideEffects); + } + @Override public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { return parent.setTile(x, y, z, tile); 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 efc4eaa51..e3e38757c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -117,6 +117,7 @@ import com.sk89q.worldedit.regions.shape.RegionShape; import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.eventbus.EventBus; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; @@ -572,6 +573,20 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { disableHistory(enabled); } + /** + * Set which block updates should occur. + * + * @param sideEffectSet side effects to enable + */ + public void setSideEffectApplier(SideEffectSet sideEffectSet) { + //Do nothing; TODO: SideEffects currently not fully implemented in FAWE. + } + + public SideEffectSet getSideEffectApplier() { + //Do nothing; TODO: SideEffects currently not fully implemented in FAWE. + return SideEffectSet.defaults(); + } + /** * Disable history (or re-enable) * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java index cc22a2972..eeb297713 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -72,6 +72,7 @@ import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.util.Identifiable; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; @@ -145,7 +146,7 @@ public class LocalSession implements TextureHolder { private transient Snapshot snapshotExperimental; private transient boolean hasCUISupport = false; private transient int cuiVersion = -1; - private transient boolean fastMode = false; + private transient SideEffectSet sideEffectSet = SideEffectSet.defaults(); private transient Mask mask; private transient Mask sourceMask; private transient TextureUtil texture; @@ -1494,7 +1495,7 @@ public class LocalSession implements TextureHolder { builder.blockBag(blockBag); } builder.command(command); - builder.fastmode(fastMode); + builder.fastmode(!this.sideEffectSet.doesApplyAny()); editSession = builder.build(); @@ -1513,7 +1514,7 @@ public class LocalSession implements TextureHolder { } private void prepareEditingExtents(EditSession editSession, Actor actor) { - editSession.setFastMode(fastMode); + editSession.setSideEffectApplier(sideEffectSet); editSession.setReorderMode(reorderMode); if (editSession.getSurvivalExtent() != null) { editSession.getSurvivalExtent().setStripNbt(!actor.hasPermission("worldedit.setnbt")); @@ -1521,13 +1522,32 @@ public class LocalSession implements TextureHolder { editSession.setTickingWatchdog(tickingWatchdog); } + /** + * Gets the side effect applier of this session. + * + * @return the side effect applier + */ + public SideEffectSet getSideEffectSet() { + return this.sideEffectSet; + } + + /** + * Sets the side effect applier for this session + * + * @param sideEffectSet the side effect applier + */ + public void setSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + /** * Checks if the session has fast mode enabled. * * @return true if fast mode is enabled */ + @Deprecated public boolean hasFastMode() { - return fastMode; + return !this.sideEffectSet.doesApplyAny(); } /** @@ -1535,8 +1555,9 @@ public class LocalSession implements TextureHolder { * * @param fastMode true if fast mode is enabled */ + @Deprecated public void setFastMode(boolean fastMode) { - this.fastMode = fastMode; + this.sideEffectSet = fastMode ? SideEffectSet.none() : SideEffectSet.defaults(); } /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java index 91e6c76b4..923add56e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java @@ -49,10 +49,13 @@ import java.util.ArrayList; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.formatting.component.PaginationBox; +import com.sk89q.worldedit.util.formatting.component.SideEffectBox; +import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.format.TextColor; -import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.item.ItemType; import java.io.FileNotFoundException; @@ -142,24 +145,62 @@ public class GeneralCommands { @Command( name = "/fast", - desc = "Toggle fast mode" + desc = "Toggle fast mode side effects" ) @CommandPermissions("worldedit.fast") public void fast(Actor actor, LocalSession session, - @Arg(desc = "The new fast mode state", def = "") - Boolean fastMode) { - boolean hasFastMode = session.hasFastMode(); - if (fastMode != null && fastMode == hasFastMode) { - actor.printError(TranslatableComponent.of(fastMode ? "worldedit.fast.enabled.already" : "worldedit.fast.disabled.already")); - return; + @Arg(desc = "The side effect", def = "") + SideEffect sideEffect, + @Arg(desc = "The new side effect state", def = "") + SideEffect.State newState, + @Switch(name = 'h', desc = "Show the info box") + boolean showInfoBox) throws WorldEditException { + if (sideEffect != null) { + SideEffect.State currentState = session.getSideEffectSet().getState(sideEffect); + if (newState != null && newState == currentState) { + if (!showInfoBox) { + actor.printError(TranslatableComponent.of( + "worldedit.fast.sideeffect.already-set", + TranslatableComponent.of(sideEffect.getDisplayName()), + TranslatableComponent.of(newState.getDisplayName()) + )); + } + return; + } + + if (newState != null) { + session.setSideEffectSet(session.getSideEffectSet().with(sideEffect, newState)); + if (!showInfoBox) { + actor.printInfo(TranslatableComponent.of( + "worldedit.fast.sideeffect.set", + TranslatableComponent.of(sideEffect.getDisplayName()), + TranslatableComponent.of(newState.getDisplayName()) + )); + } + } else { + actor.printInfo(TranslatableComponent.of( + "worldedit.fast.sideeffect.get", + TranslatableComponent.of(sideEffect.getDisplayName()), + TranslatableComponent.of(currentState.getDisplayName()) + )); + } + } else if (newState != null) { + SideEffectSet applier = session.getSideEffectSet(); + for (SideEffect sideEffectEntry : SideEffect.values()) { + applier = applier.with(sideEffectEntry, newState); + } + session.setSideEffectSet(applier); + if (!showInfoBox) { + actor.printInfo(TranslatableComponent.of( + "worldedit.fast.sideeffect.set-all", + TranslatableComponent.of(newState.getDisplayName()) + )); + } } - if (hasFastMode) { - session.setFastMode(false); - actor.printInfo(TranslatableComponent.of("worldedit.fast.disabled")); - } else { - session.setFastMode(true); - actor.printInfo(TranslatableComponent.of("worldedit.fast.enabled")); + if (sideEffect == null || showInfoBox) { + SideEffectBox sideEffectBox = new SideEffectBox(session.getSideEffectSet()); + actor.print(sideEffectBox.create(1)); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java index 7dc2192c7..f0f8e8ddd 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java @@ -23,6 +23,7 @@ import com.boydti.fawe.object.brush.scroll.Scroll; import com.google.common.collect.ImmutableSet; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.command.util.HookMode; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.TreeGenerator; import org.enginehub.piston.CommandManager; import org.enginehub.piston.converter.ArgumentConverter; @@ -30,6 +31,7 @@ import org.enginehub.piston.converter.MultiKeyConverter; import org.enginehub.piston.inject.Key; import javax.annotation.Nullable; + import java.util.EnumSet; import java.util.Locale; import java.util.Set; @@ -48,6 +50,11 @@ public final class EnumConverter { full(EditSession.ReorderMode.class, r -> ImmutableSet.of(r.getDisplayName()), null)); + commandManager.registerConverter(Key.of(SideEffect.State.class), + MultiKeyConverter.from( + EnumSet.of(SideEffect.State.OFF, SideEffect.State.ON), + r -> ImmutableSet.of(r.name().toLowerCase(Locale.US)), + null)); commandManager.registerConverter(Key.of(HookMode.class), basic(HookMode.class)); commandManager.registerConverter(Key.of(Scroll.Action.class), diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/SideEffectConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/SideEffectConverter.java new file mode 100644 index 000000000..fd0ab684f --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/SideEffectConverter.java @@ -0,0 +1,75 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix; + +import com.google.common.collect.ImmutableList; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.EntityRemover; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.converter.ArgumentConverter; +import org.enginehub.piston.converter.ConversionResult; +import org.enginehub.piston.converter.FailedConversion; +import org.enginehub.piston.converter.SuccessfulConversion; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.inject.Key; + +import java.util.Collection; +import java.util.List; +import java.util.Locale; + +public class SideEffectConverter implements ArgumentConverter { + + public static void register(CommandManager commandManager) { + commandManager.registerConverter(Key.of(SideEffect.class), new SideEffectConverter()); + } + + private final TextComponent choices = TextComponent.of("any side effect"); + + private SideEffectConverter() { + } + + private Collection getSideEffects() { + return WorldEdit.getInstance().getPlatformManager().getSupportedSideEffects(); + } + + @Override + public Component describeAcceptableArguments() { + return choices; + } + + @Override + public List getSuggestions(String input, InjectedValueAccess context) { + return limitByPrefix(getSideEffects().stream().map(sideEffect -> sideEffect.name().toLowerCase(Locale.US)), input); + } + + @Override + public ConversionResult convert(String argument, InjectedValueAccess context) { + try { + return SuccessfulConversion.fromSingle(SideEffect.valueOf(argument.toUpperCase(Locale.US))); + } catch (Exception e) { + return FailedConversion.from(e); + } + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java index d82ed52c1..1f92543aa 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java @@ -21,14 +21,17 @@ package com.sk89q.worldedit.extension.platform; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.registry.Registries; import org.enginehub.piston.CommandManager; import javax.annotation.Nullable; + import java.util.List; import java.util.Map; +import java.util.Set; /** * Represents a platform that WorldEdit has been implemented for. @@ -174,4 +177,5 @@ public interface Platform { */ Map getCapabilities(); + Set getSupportedSideEffects(); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java index 305c7f540..2b533f170 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java @@ -94,6 +94,7 @@ import com.sk89q.worldedit.command.argument.ExpressionConverter; import com.sk89q.worldedit.command.argument.FactoryConverter; import com.sk89q.worldedit.command.argument.RegionFactoryConverter; import com.sk89q.worldedit.command.argument.RegistryConverter; +import com.sk89q.worldedit.command.argument.SideEffectConverter; import com.sk89q.worldedit.command.argument.VectorConverter; import com.sk89q.worldedit.command.argument.WorldConverter; import com.sk89q.worldedit.command.argument.ZonedDateTimeConverter; @@ -255,6 +256,7 @@ public final class PlatformCommandManager { RegionFactoryConverter.register(commandManager); WorldConverter.register(commandManager); ExpressionConverter.register(commandManager); + SideEffectConverter.register(commandManager); registerBindings(new ConsumeBindings(worldEdit, this)); registerBindings(new PrimitiveBindings(worldEdit)); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 9422a9abf..6ce088ac3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -48,9 +48,11 @@ import com.sk89q.worldedit.event.platform.PlayerInputEvent; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.eventbus.Subscribe; import com.sk89q.worldedit.world.World; import java.util.ArrayList; +import java.util.Collection; import java.util.EnumMap; import java.util.Iterator; import java.util.List; @@ -304,6 +306,10 @@ public class PlatformManager { return queryCapability(Capability.CONFIGURATION).getConfiguration(); } + public Collection getSupportedSideEffects() { + return queryCapability(Capability.WORLD_EDITING).getSupportedSideEffects(); + } + @Subscribe public void handlePlatformReady(PlatformReadyEvent event) { choosePreferred(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ExtentBuffer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ExtentBuffer.java index 4ae9a3ad5..699b73d43 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ExtentBuffer.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ExtentBuffer.java @@ -21,17 +21,16 @@ package com.sk89q.worldedit.extent.buffer; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.collect.Maps; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.AbstractBufferingExtent; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.collection.BlockMap; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; import java.util.Map; -import java.util.Optional; /** * Buffers changes to an {@link Extent} and allows retrieval of the changed blocks, @@ -39,7 +38,7 @@ import java.util.Optional; */ public class ExtentBuffer extends AbstractBufferingExtent { - private final Map buffer = Maps.newHashMap(); + private final Map buffer = BlockMap.create(); private final Mask mask; /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java index 60cc68cf7..8b34e4778 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.extent.reorder; -import com.google.common.collect.ImmutableSortedSet; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.AbstractBufferingExtent; import com.sk89q.worldedit.extent.Extent; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/FastModeExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SideEffectExtent.java similarity index 63% rename from worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/FastModeExtent.java rename to worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SideEffectExtent.java index a29eff306..1a6b78505 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/FastModeExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SideEffectExtent.java @@ -27,67 +27,38 @@ import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.RunContext; import com.sk89q.worldedit.math.BlockVector2; 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.World; +import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.block.BlockTypes; import java.util.HashSet; import java.util.Iterator; -import java.util.List; +import java.util.Map; import java.util.Set; /** - * Implements "fast mode" which may skip physics, lighting, etc. + * An extent that sets blocks in the world, with a {@link SideEffectSet}. */ -public class FastModeExtent extends AbstractDelegateExtent { +public class SideEffectExtent extends AbstractDelegateExtent { private final World world; - private final Set positions = new HashSet<>(); + private final Map positions = BlockMap.create(); private final Set dirtyChunks = new HashSet<>(); - private boolean enabled = true; + private SideEffectSet sideEffectSet = SideEffectSet.defaults(); private boolean postEditSimulation; - /** - * Create a new instance with fast mode enabled. - * - * @param world the world - */ - public FastModeExtent(World world) { - this(world, true); - } - /** * Create a new instance. * * @param world the world - * @param enabled true to enable fast mode */ - public FastModeExtent(World world, boolean enabled) { + public SideEffectExtent(World world) { super(world); checkNotNull(world); this.world = world; - this.enabled = enabled; - if (enabled) { - this.postEditSimulation = true; - } - } - - /** - * Return whether fast mode is enabled. - * - * @return true if fast mode is enabled - */ - public boolean isEnabled() { - return enabled; - } - - /** - * Set fast mode enable status. - * - * @param enabled true to enable fast mode - */ - public void setEnabled(boolean enabled) { - this.enabled = enabled; } public boolean isPostEditSimulationEnabled() { @@ -98,26 +69,28 @@ public class FastModeExtent extends AbstractDelegateExtent { this.postEditSimulation = enabled; } + public SideEffectSet getSideEffectSet() { + return this.sideEffectSet; + } + + public void setSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + @Override public > boolean setBlock(BlockVector3 location, B block) throws WorldEditException { - if (enabled || postEditSimulation) { + if (sideEffectSet.getState(SideEffect.LIGHTING) == SideEffect.State.DELAYED) { dirtyChunks.add(BlockVector2.at(location.getBlockX() >> 4, location.getBlockZ() >> 4)); - - if (world.setBlock(location, block, false)) { - if (!enabled && postEditSimulation) { - positions.add(location); - } - return true; - } - - return false; - } else { - return world.setBlock(location, block, true); } + if (postEditSimulation) { + positions.put(location, world.getBlock(location)); + } + + return world.setBlock(location, block, postEditSimulation ? SideEffectSet.none() : sideEffectSet); } public boolean commitRequired() { - return enabled || postEditSimulation; + return postEditSimulation || !dirtyChunks.isEmpty(); } @Override @@ -132,11 +105,11 @@ public class FastModeExtent extends AbstractDelegateExtent { world.fixAfterFastMode(dirtyChunks); } - if (!enabled && postEditSimulation) { - Iterator positionIterator = positions.iterator(); + if (postEditSimulation) { + Iterator> positionIterator = positions.entrySet().iterator(); while (run.shouldContinue() && positionIterator.hasNext()) { - BlockVector3 position = positionIterator.next(); - world.notifyAndLightBlock(position, BlockTypes.AIR.getDefaultState()); + Map.Entry position = positionIterator.next(); + world.applySideEffects(position.getKey(), position.getValue(), sideEffectSet); positionIterator.remove(); } @@ -151,5 +124,4 @@ public class FastModeExtent extends AbstractDelegateExtent { } }; } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffect.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffect.java new file mode 100644 index 000000000..c4c814fcb --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffect.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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util; + +import java.util.Locale; + +public enum SideEffect { + LIGHTING(State.ON), + NEIGHBORS(State.ON), + CONNECTIONS(State.ON), + ENTITY_AI(State.OFF), + PLUGIN_EVENTS(State.OFF); + + private final String displayName; + private final String description; + private final State defaultValue; + + SideEffect(State defaultValue) { + this.displayName = "worldedit.sideeffect." + this.name().toLowerCase(Locale.US); + this.description = "worldedit.sideeffect." + this.name().toLowerCase(Locale.US) + ".description"; + this.defaultValue = defaultValue; + } + + public String getDisplayName() { + return this.displayName; + } + + public String getDescription() { + return this.description; + } + + public State getDefaultValue() { + return this.defaultValue; + } + + public enum State { + OFF, + ON, + DELAYED; + + private final String displayName; + + State() { + this.displayName = "worldedit.sideeffect.state." + this.name().toLowerCase(Locale.US); + } + + public String getDisplayName() { + return this.displayName; + } + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffectSet.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffectSet.java new file mode 100644 index 000000000..d88b37097 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffectSet.java @@ -0,0 +1,94 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +import java.util.Arrays; +import java.util.EnumMap; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class SideEffectSet { + private static final SideEffectSet DEFAULT = new SideEffectSet( + Arrays.stream(SideEffect.values()).collect(Collectors.toMap(Function.identity(), SideEffect::getDefaultValue)) + ); + private static final SideEffectSet NONE = new SideEffectSet(); + + private final Map sideEffects; + private final Set appliedSideEffects; + private final boolean appliesAny; + + private SideEffectSet() { + this(ImmutableMap.of()); + } + + public SideEffectSet(Map sideEffects) { + this.sideEffects = Maps.immutableEnumMap(sideEffects); + + appliedSideEffects = sideEffects.entrySet() + .stream() + .filter(entry -> entry.getValue() != SideEffect.State.OFF) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + appliesAny = !appliedSideEffects.isEmpty(); + } + + public SideEffectSet with(SideEffect sideEffect, SideEffect.State state) { + Map entries = this.sideEffects.isEmpty() ? Maps.newEnumMap(SideEffect.class) : new EnumMap<>(this.sideEffects); + entries.put(sideEffect, state); + return new SideEffectSet(entries); + } + + public boolean doesApplyAny() { + return this.appliesAny; + } + + public SideEffect.State getState(SideEffect effect) { + return sideEffects.getOrDefault(effect, effect.getDefaultValue()); + } + + /** + * Gets whether this side effect is not off. + * + * This returns whether it is either delayed or on. + * + * @param effect The side effect + * @return Whether it should apply + */ + public boolean shouldApply(SideEffect effect) { + return getState(effect) != SideEffect.State.OFF; + } + + public Set getSideEffectsToApply() { + return this.appliedSideEffects; + } + + public static SideEffectSet defaults() { + return DEFAULT; + } + + public static SideEffectSet none() { + return NONE; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/SideEffectBox.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/SideEffectBox.java new file mode 100644 index 000000000..14403313e --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/SideEffectBox.java @@ -0,0 +1,88 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.formatting.component; + +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; +import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; +import com.sk89q.worldedit.util.formatting.text.event.HoverEvent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; + +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +public class SideEffectBox extends PaginationBox { + + private static List sideEffects; + + private SideEffectSet sideEffectSet; + + private static List getSideEffects() { + if (sideEffects == null) { + sideEffects = WorldEdit.getInstance().getPlatformManager().getSupportedSideEffects() + .stream() + .sorted(Comparator.comparing(Enum::name)) + .collect(Collectors.toList()); + } + + return sideEffects; + } + + public SideEffectBox(SideEffectSet sideEffectSet) { + super("Side Effects"); + + this.sideEffectSet = sideEffectSet; + } + + private static final SideEffect.State[] SHOWN_VALUES = {SideEffect.State.OFF, SideEffect.State.ON}; + + @Override + public Component getComponent(int number) { + SideEffect effect = getSideEffects().get(number); + SideEffect.State state = this.sideEffectSet.getState(effect); + + TextComponent.Builder builder = TextComponent.builder(); + builder = builder.append(TranslatableComponent.of(effect.getDisplayName(), TextColor.YELLOW) + .hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TranslatableComponent.of(effect.getDescription())))); + for (SideEffect.State uiState : SHOWN_VALUES) { + builder = builder.append(TextComponent.space()); + builder = builder.append(TranslatableComponent.of(uiState.getDisplayName(), uiState == state ? TextColor.WHITE : TextColor.GRAY) + .clickEvent(ClickEvent.runCommand("//fast -h " + effect.name().toLowerCase(Locale.US) + " " + uiState.name().toLowerCase(Locale.US))) + .hoverEvent(HoverEvent.showText(uiState == state + ? TranslatableComponent.of("worldedit.sideeffect.box.current") + : TranslatableComponent.of("worldedit.sideeffect.box.change-to", TranslatableComponent.of(uiState.getDisplayName())) + )) + ); + } + + return builder.build(); + } + + @Override + public int getComponentsSize() { + return getSideEffects().size(); + } +} 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 c333f1269..34e7179b0 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 @@ -32,6 +32,7 @@ import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.weather.WeatherType; import com.sk89q.worldedit.world.block.BlockType; @@ -57,7 +58,7 @@ public abstract class AbstractWorld implements World { @Override public final > boolean setBlock(BlockVector3 pt, B block) throws WorldEditException { - return setBlock(pt, block, true); + return setBlock(pt, block, SideEffectSet.defaults()); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java index f37665232..f3d6dd18a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java @@ -23,6 +23,7 @@ import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.boydti.fawe.beta.implementation.blocks.NullChunkGet; import com.sk89q.jnbt.CompoundTag; +import com.google.common.collect.ImmutableSet; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; @@ -35,6 +36,8 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator.TreeType; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; @@ -48,6 +51,7 @@ import com.sk89q.worldedit.world.weather.WeatherTypes; import javax.annotation.Nullable; import java.util.Collections; import java.util.List; +import java.util.Set; /** * A null implementation of {@link World} that drops all changes and @@ -71,13 +75,14 @@ public class NullWorld extends AbstractWorld { } @Override - public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException { + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { return false; } @Override - public boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException { - return false; + public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) + throws WorldEditException { + return ImmutableSet.of(); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java index 5c6ba7df3..9388dcace 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java @@ -37,15 +37,19 @@ import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.Keyed; import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.weather.WeatherType; -import javax.annotation.Nullable; import java.nio.file.Path; import java.util.Locale; +import java.util.Set; + +import javax.annotation.Nullable; /** * Represents a world (dimension). @@ -112,10 +116,28 @@ public interface World extends Extent, Keyed, IChunkCache { * @param notifyAndLight true to to notify and light * @return true if the block was successfully set (return value may not be accurate) */ + @Deprecated default > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException { - return setBlock(position, block); + return setBlock(position, block, notifyAndLight ? SideEffectSet.defaults() : SideEffectSet.none()); } + /** + * Similar to {@link Extent#setBlock(BlockVector3, BlockStateHolder)} but a + * {@code sideEffects} parameter indicates which side effects should be applied + * to the block. This includes block updates, lighting, and others. See {@link SideEffect} + * for a full list. + * + *

Not all implementations support all side effects. Use + * {@link Platform#getSupportedSideEffects()} for a list of supported side effects. + * Non-supported side effects will be ignored.

+ * + * @param position position of the block + * @param block block to set + * @param sideEffects which side effects to perform + * @return true if the block was successfully set (return value may not be accurate) + */ + > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException; + /** * Notifies the simulation that the block at the given location has * been changed and it must be re-lighted (and issue other events). @@ -124,7 +146,20 @@ public interface World extends Extent, Keyed, IChunkCache { * @param previousType the type of the previous block that was there * @return true if the block was successfully notified */ - boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException; + @Deprecated + default boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException { + return !applySideEffects(position, previousType, SideEffectSet.defaults()).isEmpty(); + } + + /** + * Applies a set of side effects on the given block. + * + * @param position position of the block + * @param previousType the type of the previous block that was there + * @param sideEffectSet which side effects to perform + * @return a set of side effects that were applied + */ + Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException; /** * Get the light level at the given block. diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index fe79f060d..4708d4596 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -210,6 +210,10 @@ "worldedit.fast.enabled": "Fast mode enabled. Lighting in the affected chunks may be wrong and/or you may need to rejoin to see changes.", "worldedit.fast.disabled.already": "Fast mode already disabled.", "worldedit.fast.enabled.already": "Fast mode already enabled.", + "worldedit.fast.sideeffect.set": "Side effect \"{0}\" set to {1}", + "worldedit.fast.sideeffect.get": "Side effect \"{0}\" is set to {1}", + "worldedit.fast.sideeffect.already-set": "Side effect \"{0}\" is already {1}", + "worldedit.fast.sideeffect.set-all": "All side effects set to {0}", "worldedit.reorder.current": "The reorder mode is {0}", "worldedit.reorder.set": "The reorder mode is now {0}", "worldedit.gmask.disabled": "Global mask disabled.", @@ -494,6 +498,22 @@ "worldedit.selection.sphere.explain.secondary": "Radius set to {0}.", "worldedit.selection.sphere.explain.secondary-defined": "Radius set to {0} ({1}).", + "worldedit.sideeffect.lighting": "Lighting", + "worldedit.sideeffect.lighting.description": "Updates block lighting", + "worldedit.sideeffect.neighbors": "Neighbors", + "worldedit.sideeffect.neighbors.description": "Notifies nearby blocks of changes", + "worldedit.sideeffect.connections": "Connections", + "worldedit.sideeffect.connections.description": "Updates connections for blocks like fences", + "worldedit.sideeffect.entity_ai": "Entity AI", + "worldedit.sideeffect.entity_ai.description": "Updates Entity AI paths for the block changes", + "worldedit.sideeffect.plugin_events": "Plugin Events", + "worldedit.sideeffect.plugin_events.description": "Tells other plugins/mods about these changes when applicable", + "worldedit.sideeffect.state.on": "On", + "worldedit.sideeffect.state.delayed": "Delayed", + "worldedit.sideeffect.state.off": "Off", + "worldedit.sideeffect.box.current": "Current", + "worldedit.sideeffect.box.change-to": "Click to set to {0}", + "worldedit.help.command-not-found": "The command '{0}' could not be found.", "worldedit.help.no-subcommands": "'{0}' has no sub-commands. (Maybe '{1}' is for a parameter?)", "worldedit.help.subcommand-not-found": "The sub-command '{0}' under '{1}' could not be found.", diff --git a/worldedit-fabric/build.gradle.kts b/worldedit-fabric/build.gradle.kts index be85099fa..1cf6df8dd 100644 --- a/worldedit-fabric/build.gradle.kts +++ b/worldedit-fabric/build.gradle.kts @@ -61,16 +61,12 @@ tasks.named("processResources") { } } -<<<<<<< HEAD -addJarManifest(includeClasspath = true) -======= tasks.named("jar") { manifest { attributes("Class-Path" to CLASSPATH, "WorldEdit-Version" to project.version) } } ->>>>>>> 18a55bc14... Add new experimental snapshot API (#524) tasks.named("shadowJar") { archiveClassifier.set("dist-dev") diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java index beb6d5208..7fb18b0e7 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.fabric; +import com.google.common.collect.Sets; import com.sk89q.worldedit.command.util.PermissionCondition; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.AbstractPlatform; @@ -27,13 +28,13 @@ import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.MultiUserPlatform; import com.sk89q.worldedit.extension.platform.Preference; import com.sk89q.worldedit.extension.platform.Watchdog; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.registry.Registries; import net.minecraft.SharedConstants; import net.minecraft.server.MinecraftServer; import net.minecraft.server.PlayerManager; -import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.MinecraftDedicatedServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; @@ -200,6 +201,18 @@ class FabricPlatform extends AbstractPlatform implements MultiUserPlatform { return capabilities; } + private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( + SideEffect.CONNECTIONS, + SideEffect.ENTITY_AI, + SideEffect.LIGHTING, + SideEffect.NEIGHBORS + ); + + @Override + public Set getSupportedSideEffects() { + return SUPPORTED_SIDE_EFFECTS; + } + @Override public Collection getConnectedUsers() { List users = new ArrayList<>(); 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 886523df5..e085d2326 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 @@ -24,6 +24,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.google.common.collect.Sets; import com.google.common.io.Files; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; @@ -42,6 +43,8 @@ import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator.TreeType; import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.biome.BiomeType; @@ -61,6 +64,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.item.ItemUsageContext; import net.minecraft.server.MinecraftServer; import net.minecraft.server.WorldGenerationProgressListener; +import net.minecraft.server.world.ChunkHolder; import net.minecraft.server.world.ServerChunkManager; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.ActionResult; @@ -71,6 +75,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.world.World; import net.minecraft.world.WorldSaveHandler; +import net.minecraft.world.biome.DefaultBiomeFeatures; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.ChunkManager; import net.minecraft.world.chunk.ChunkStatus; @@ -104,6 +109,7 @@ import java.util.Locale; import java.util.Optional; import java.util.OptionalInt; import java.util.Random; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; @@ -185,8 +191,44 @@ public class FabricWorld extends AbstractWorld { return null; } + public void markAndNotifyBlock(World world, BlockPos pos, @Nullable WorldChunk worldChunk, net.minecraft.block.BlockState blockState, + net.minecraft.block.BlockState state, SideEffectSet sideEffectSet) { + Block block = state.getBlock(); + net.minecraft.block.BlockState blockState2 = world.getBlockState(pos); + if (blockState2 == state) { + if (blockState != blockState2) { + world.checkBlockRerender(pos, blockState, blockState2); + } + + if (world.isClient || worldChunk.getLevelType() != null && worldChunk.getLevelType().isAfter(ChunkHolder.LevelType.TICKING)) { + if (sideEffectSet.shouldApply(SideEffect.ENTITY_AI)) { + world.updateListeners(pos, blockState, state, UPDATE | NOTIFY); + } else { + // If we want to skip entity AI, just call the chunk dirty flag. + ((ServerChunkManager) world.getChunkManager()).markForUpdate(pos); + } + } + + if (!world.isClient && sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) { + world.updateNeighbors(pos, blockState.getBlock()); + if (state.hasComparatorOutput()) { + world.updateHorizontalAdjacent(pos, block); + } + } + + if (sideEffectSet.shouldApply(SideEffect.CONNECTIONS)) { + blockState.method_11637(world, pos, 2); + state.updateNeighborStates(world, pos, 2); + state.method_11637(world, pos, 2); + } + + // This is disabled for other platforms, but keep it for mods. + world.onBlockChanged(pos, blockState, blockState2); + } + } + @Override - public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException { + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { checkNotNull(position); checkNotNull(block); @@ -196,7 +238,7 @@ public class FabricWorld extends AbstractWorld { int z = position.getBlockZ(); // First set the block - Chunk chunk = world.getChunk(x >> 4, z >> 4); + WorldChunk chunk = world.getChunk(x >> 4, z >> 4); BlockPos pos = new BlockPos(x, y, z); net.minecraft.block.BlockState old = chunk.getBlockState(pos); OptionalInt stateId = BlockStateIdAccess.getBlockStateId(block.toImmutableState()); @@ -221,26 +263,27 @@ public class FabricWorld extends AbstractWorld { } } - if (successful && notifyAndLight) { - world.getChunkManager().getLightingProvider().enqueueLightUpdate(pos); - world.scheduleBlockRender(pos, old, newState); - world.updateListeners(pos, old, newState, UPDATE | NOTIFY); - world.updateNeighbors(pos, newState.getBlock()); - if (old.hasComparatorOutput()) { - world.updateHorizontalAdjacent(pos, newState.getBlock()); + if (successful) { + if (sideEffects.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { + world.getChunkManager().getLightingProvider().checkBlock(pos); } + markAndNotifyBlock(world, pos, chunk, old, newState, sideEffects); } return successful; } @Override - public boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException { + public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException { BlockPos pos = new BlockPos(position.getX(), position.getY(), position.getZ()); - net.minecraft.block.BlockState state = getWorld().getBlockState(pos); - getWorld().updateListeners(pos, FabricAdapter.adapt(previousType), state, 1 | 2); - getWorld().updateNeighbors(pos, state.getBlock()); - return true; + net.minecraft.block.BlockState oldData = FabricAdapter.adapt(previousType); + net.minecraft.block.BlockState newData = getWorld().getBlockState(pos); + + if (sideEffectSet.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { + getWorld().getChunkManager().getLightingProvider().checkBlock(pos); + } + markAndNotifyBlock(getWorld(), pos, null, oldData, newData, sideEffectSet); // Update + return Sets.intersection(FabricWorldEdit.inst.getPlatform().getSupportedSideEffects(), sideEffectSet.getSideEffectsToApply()); } @Override diff --git a/worldedit-forge/build.gradle.kts b/worldedit-forge/build.gradle.kts index 23d52162d..f3b938ca7 100644 --- a/worldedit-forge/build.gradle.kts +++ b/worldedit-forge/build.gradle.kts @@ -77,7 +77,12 @@ tasks.named("processResources") { } } -addJarManifest(includeClasspath = false) +tasks.named("jar") { + manifest { + attributes("Class-Path" to CLASSPATH, + "WorldEdit-Version" to project.version) + } +} tasks.named("shadowJar") { dependencies { diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java index e1af09064..b844425eb 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java @@ -27,6 +27,7 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.MultiUserPlatform; import com.sk89q.worldedit.extension.platform.Preference; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.registry.Registries; @@ -206,6 +207,18 @@ class ForgePlatform extends AbstractPlatform implements MultiUserPlatform { return capabilities; } + private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( + SideEffect.CONNECTIONS, + SideEffect.ENTITY_AI, + SideEffect.LIGHTING, + SideEffect.NEIGHBORS + ); + + @Override + public Set getSupportedSideEffects() { + return SUPPORTED_SIDE_EFFECTS; + } + @Override public Collection getConnectedUsers() { List users = new ArrayList<>(); diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java index f9096578b..5d69754a6 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit.forge; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; @@ -96,13 +98,13 @@ import net.minecraft.world.gen.feature.SwampTreeFeature; import net.minecraft.world.gen.feature.TallTaigaTreeFeature; import net.minecraft.world.gen.feature.TreeFeature; +import net.minecraft.world.server.ChunkHolder; import net.minecraft.world.server.ServerChunkProvider; import net.minecraft.world.server.ServerWorld; import net.minecraft.world.storage.SaveHandler; import net.minecraft.world.storage.WorldInfo; import net.minecraftforge.common.DimensionManager; -import javax.annotation.Nullable; import java.io.File; import java.io.IOException; import java.lang.ref.WeakReference; @@ -117,7 +119,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkNotNull; +import javax.annotation.Nullable; /** * An adapter to Minecraft worlds for WorldEdit. @@ -130,7 +132,7 @@ public class ForgeWorld extends AbstractWorld { private static final net.minecraft.block.BlockState JUNGLE_LOG = Blocks.JUNGLE_LOG.getDefaultState(); private static final net.minecraft.block.BlockState JUNGLE_LEAF = Blocks.JUNGLE_LEAVES.getDefaultState().with(LeavesBlock.PERSISTENT, Boolean.TRUE); private static final net.minecraft.block.BlockState JUNGLE_SHRUB = Blocks.OAK_LEAVES.getDefaultState().with(LeavesBlock.PERSISTENT, Boolean.TRUE); - + private final WeakReference worldRef; /** @@ -192,8 +194,51 @@ public class ForgeWorld extends AbstractWorld { return null; } + /** + * This is a heavily modified function stripped from MC to apply worldedit-modifications. + * + * @see World#markAndNotifyBlock + */ + public void markAndNotifyBlock(World world, BlockPos pos, @Nullable Chunk chunk, net.minecraft.block.BlockState blockstate, + net.minecraft.block.BlockState newState, SideEffectSet sideEffectSet) { + Block block = newState.getBlock(); + net.minecraft.block.BlockState blockstate1 = world.getBlockState(pos); + if (blockstate1 == newState) { + if (blockstate != blockstate1) { + world.markBlockRangeForRenderUpdate(pos, blockstate, blockstate1); + } + + // Remove redundant branches + if (world.isRemote || chunk == null || chunk.getLocationType().isAtLeast(ChunkHolder.LocationType.TICKING)) { + if (sideEffectSet.shouldApply(SideEffect.ENTITY_AI)) { + world.notifyBlockUpdate(pos, blockstate, newState, UPDATE | NOTIFY); + } else { + // If we want to skip entity AI, just call the chunk dirty flag. + ((ServerChunkProvider) world.getChunkProvider()).markBlockChanged(pos); + } + } + + if (!world.isRemote && sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) { + world.notifyNeighbors(pos, blockstate.getBlock()); + if (newState.hasComparatorInputOverride()) { + world.updateComparatorOutputLevel(pos, block); + } + } + + // Make connection updates optional + if (sideEffectSet.shouldApply(SideEffect.CONNECTIONS)) { + blockstate.updateDiagonalNeighbors(world, pos, 2); + newState.updateNeighbors(world, pos, 2); + newState.updateDiagonalNeighbors(world, pos, 2); + } + + // This is disabled for other platforms, but keep it for mods. + world.onBlockStateChange(pos, blockstate, blockstate1); + } + } + @Override - public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException { + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { checkNotNull(position); checkNotNull(block); @@ -224,19 +269,27 @@ public class ForgeWorld extends AbstractWorld { } } - if (successful && notifyAndLight) { - world.getChunkProvider().getLightManager().checkBlock(pos); - world.markAndNotifyBlock(pos, chunk, old, newState, UPDATE | NOTIFY); + if (successful) { + if (sideEffects.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { + world.getChunkProvider().getLightManager().checkBlock(pos); + } + markAndNotifyBlock(world, pos, chunk, old, newState, sideEffects); } return successful; } @Override - public boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException { + public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException { BlockPos pos = new BlockPos(position.getX(), position.getY(), position.getZ()); - getWorld().notifyBlockUpdate(pos, ForgeAdapter.adapt(previousType), getWorld().getBlockState(pos), 1 | 2); - return true; + net.minecraft.block.BlockState oldData = ForgeAdapter.adapt(previousType); + net.minecraft.block.BlockState newData = getWorld().getBlockState(pos); + + if (sideEffectSet.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { + getWorld().getChunkProvider().getLightManager().checkBlock(pos); + } + markAndNotifyBlock(getWorld(), pos, null, oldData, newData, sideEffectSet); // Update + return Sets.intersection(ForgeWorldEdit.inst.getPlatform().getSupportedSideEffects(), sideEffectSet.getSideEffectsToApply()); } @Override diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java index 33849e915..ddfaad17b 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java @@ -20,6 +20,8 @@ package com.sk89q.worldedit.sponge; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.CommandEvent; @@ -31,6 +33,7 @@ import com.sk89q.worldedit.extension.platform.MultiUserPlatform; import com.sk89q.worldedit.extension.platform.Preference; import com.sk89q.worldedit.internal.command.CommandUtil; import com.sk89q.worldedit.sponge.config.SpongeConfiguration; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.registry.Registries; import org.enginehub.piston.Command; @@ -50,6 +53,7 @@ import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import static java.util.stream.Collectors.toList; @@ -192,6 +196,11 @@ class SpongePlatform extends AbstractPlatform implements MultiUserPlatform { return capabilities; } + @Override + public Set getSupportedSideEffects() { + return ImmutableSet.of(); + } + @Override public Collection getConnectedUsers() { List users = new ArrayList<>();