From f2bc8d86fcd43c787c9864a5d11b93fd1701803e Mon Sep 17 00:00:00 2001 From: IronApollo Date: Fri, 26 Jun 2020 19:08:45 -0400 Subject: [PATCH 01/11] Work towards 1.16 compatibility This commit will allow the branch to build properly but the plugin will not function properly due to the lack of a proper adapter implementation. Proceeding will require the implementation of the SideEffects system from upstream (https://github.com/EngineHub/WorldEdit/commit/865c3a24d25156cbffbf44630506a08d2ff4755a#diff-8fd33296e427c87d0296ad7f3ccc050a). --- .../mc1_16_1/BukkitAdapter_1_16_1.java | 5 + .../mc1_16_1/BukkitGetBlocks_1_16_1.java | 4 +- .../worldedit/bukkit/WorldEditPlugin.java | 2 + .../adapter/impl/FAWE_Spigot_v1_16_R1.java | 165 +++++++++--------- 4 files changed, 94 insertions(+), 82 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java index 74ae46eb2..7b1549d65 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java @@ -38,6 +38,8 @@ public final class BukkitAdapter_1_16_1 extends NMSAdapter { public final static Field fieldPalette; public final static Field fieldSize; + public final static Field fieldBitsPerEntry; + public final static Field fieldFluidCount; public final static Field fieldTickingBlockCount; public final static Field fieldNonEmptyBlockCount; @@ -61,6 +63,9 @@ public final class BukkitAdapter_1_16_1 extends NMSAdapter { fieldPalette = DataPaletteBlock.class.getDeclaredField("h"); fieldPalette.setAccessible(true); + fieldBitsPerEntry = DataBits.class.getDeclaredField("c"); + fieldBitsPerEntry.setAccessible(true); + fieldFluidCount = ChunkSection.class.getDeclaredField("e"); fieldFluidCount.setAccessible(true); fieldTickingBlockCount = ChunkSection.class.getDeclaredField("tickingBlockCount"); diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java index 110a47298..b07589339 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java @@ -465,7 +465,7 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { tag.set("x", NBTTagInt.a(x)); tag.set("y", NBTTagInt.a(y)); tag.set("z", NBTTagInt.a(z)); - tileEntity.load(tag); + tileEntity.load(tileEntity.getBlock(), tag); } } } @@ -550,7 +550,7 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { final DataBits bits = (DataBits) BukkitAdapter_1_16_1.fieldBits.get(blocks); final DataPalette palette = (DataPalette) BukkitAdapter_1_16_1.fieldPalette.get(blocks); - final int bitsPerEntry = bits.c(); + final int bitsPerEntry = (int) BukkitAdapter_1_16_1.fieldBitsPerEntry.get(bits); final long[] blockStates = bits.a(); new BitArray(bitsPerEntry, 4096, blockStates).toRaw(data); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index ae66a73af..3124b04f6 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -34,6 +34,7 @@ import com.sk89q.worldedit.bukkit.adapter.AdapterLoadException; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplLoader; import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_15_R2; +import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_16_R1; import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.event.platform.CommandSuggestionEvent; import com.sk89q.worldedit.event.platform.PlatformReadyEvent; @@ -371,6 +372,7 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter try { adapterLoader.addClass(FAWE_Spigot_v1_14_R4.class); adapterLoader.addClass(FAWE_Spigot_v1_15_R2.class); + adapterLoader.addClass(FAWE_Spigot_v1_16_R1.class); } catch (Throwable throwable) { throwable.printStackTrace(); } 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 5cf63b4d3..695aeca79 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 @@ -27,6 +27,9 @@ import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.boydti.fawe.beta.implementation.queue.SingleThreadQueueExtent; import com.boydti.fawe.bukkit.adapter.mc1_16_1.BlockMaterial_1_16_1; +import com.boydti.fawe.bukkit.adapter.mc1_16_1.BukkitAdapter_1_16_1; +import com.boydti.fawe.bukkit.adapter.mc1_16_1.BukkitGetBlocks_1_16_1; +import com.boydti.fawe.bukkit.adapter.mc1_16_1.MapChunkUtil_1_16_1; import com.boydti.fawe.bukkit.adapter.mc1_16_1.nbt.LazyCompoundTag_1_16_1; import com.google.common.io.Files; import com.sk89q.jnbt.CompoundTag; @@ -49,18 +52,18 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.*; import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.registry.BlockMaterial; -import net.minecraft.server.v1_15_R1.*; +import net.minecraft.server.v1_16_R1.*; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World.Environment; import org.bukkit.block.data.BlockData; -import org.bukkit.craftbukkit.v1_15_R1.CraftChunk; -import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_15_R1.block.CraftBlock; -import org.bukkit.craftbukkit.v1_15_R1.block.data.CraftBlockData; -import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity; -import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_16_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_16_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R1.block.CraftBlock; +import org.bukkit.craftbukkit.v1_16_R1.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_16_R1.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; import org.bukkit.entity.Player; import org.bukkit.generator.ChunkGenerator; @@ -192,7 +195,7 @@ public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements I tag.set("x", NBTTagInt.a(x)); tag.set("y", NBTTagInt.a(y)); tag.set("z", NBTTagInt.a(z)); - tileEntity.load(tag); // readTagIntoTileEntity - load data + tileEntity.load(tileEntity.getBlock(), tag); // readTagIntoTileEntity - load data } } } else { @@ -368,78 +371,80 @@ public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements I @Override public boolean regenerate(org.bukkit.World world, Region region, EditSession editSession) { - WorldServer originalWorld = ((CraftWorld) world).getHandle(); - ChunkProviderServer provider = originalWorld.getChunkProvider(); - if (!(provider instanceof ChunkProviderServer)) { - return false; - } +// WorldServer originalWorld = ((CraftWorld) world).getHandle(); +// ChunkProviderServer provider = originalWorld.getChunkProvider(); +// if (!(provider instanceof ChunkProviderServer)) { +// return false; +// } +// +// File saveFolder = Files.createTempDir(); +// // register this just in case something goes wrong +// // normally it should be deleted at the end of this method +// saveFolder.deleteOnExit(); +// try { +// MinecraftServer server = originalWorld.getServer().getServer(); +// Convertable.ConversionSession originalDataManager = server.convertable; +//// Convertable.ConversionSession saveHandler = new Convertable.ConversionSession(world.getName(), world.); +// WorldData newWorldData = new WorldData(originalWorld.worldData.a((NBTTagCompound) null), +// server.dataConverterManager, getDataVersion(), null); +// newWorldData.setName(UUID.randomUUID().toString()); +// +// ChunkGenerator gen = world.getGenerator(); +// Environment env = world.getEnvironment(); +// try (WorldServer freshWorld = new WorldServer(server, +// server.executorService, originalDataManager, +// newWorldData, +// originalWorld.worldProvider.getDimensionManager(), +// originalWorld.getMethodProfiler(), +// server.worldLoadListenerFactory.create(11), +// env, +// gen){ +// @Override +// public boolean addEntityChunk(Entity entity) { +// //Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously +// return false; +// } +// }) { +// +// // Pre-gen all the chunks +// // We need to also pull one more chunk in every direction +// Fawe.get().getQueueHandler().startSet(true); +// try { +// IQueueExtent extent = new SingleThreadQueueExtent(); +// extent.init(null, (x, z) -> new BukkitGetBlocks_1_16_1(freshWorld, x, z) { +// @Override +// public Chunk ensureLoaded(World nmsWorld, int X, int Z) { +// Chunk cached = nmsWorld.getChunkIfLoaded(X, Z); +// if (cached != null) return cached; +// Future future = Fawe.get().getQueueHandler().sync((Supplier) () -> freshWorld.getChunkAt(X, Z)); +// while (!future.isDone()) { +// // this feels so dirty +// freshWorld.getChunkProvider().runTasks(); +// } +// try { +// return future.get(); +// } catch (InterruptedException | ExecutionException e) { +// throw new RuntimeException(e); +// } +// } +// }, null); +// for (BlockVector3 vec : region) { +// editSession.setBlock(vec, extent.getFullBlock(vec)); +// } +// } finally { +// Fawe.get().getQueueHandler().endSet(true); +// } +// } catch (IOException e) { +// throw new RuntimeException(e); +// } +// } catch (MaxChangedBlocksException e) { +// throw new RuntimeException(e); +// } finally { +// saveFolder.delete(); +// } +// return true; - File saveFolder = Files.createTempDir(); - // register this just in case something goes wrong - // normally it should be deleted at the end of this method - saveFolder.deleteOnExit(); - try { - MinecraftServer server = originalWorld.getServer().getServer(); - WorldNBTStorage originalDataManager = originalWorld.getDataManager(); - WorldNBTStorage saveHandler = new WorldNBTStorage(saveFolder, originalDataManager.getDirectory().getName(), server, originalDataManager.getDataFixer()); - WorldData newWorldData = new WorldData(originalWorld.worldData.a((NBTTagCompound) null), - server.dataConverterManager, getDataVersion(), null); - newWorldData.setName(UUID.randomUUID().toString()); - - ChunkGenerator gen = world.getGenerator(); - Environment env = world.getEnvironment(); - try (WorldServer freshWorld = new WorldServer(server, - server.executorService, saveHandler, - newWorldData, - originalWorld.worldProvider.getDimensionManager(), - originalWorld.getMethodProfiler(), - server.worldLoadListenerFactory.create(11), - env, - gen){ - @Override - public boolean addEntityChunk(Entity entity) { - //Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously - return false; - } - }) { - - // Pre-gen all the chunks - // We need to also pull one more chunk in every direction - Fawe.get().getQueueHandler().startSet(true); - try { - IQueueExtent extent = new SingleThreadQueueExtent(); - extent.init(null, (x, z) -> new BukkitGetBlocks_1_16_1(freshWorld, x, z) { - @Override - public Chunk ensureLoaded(World nmsWorld, int X, int Z) { - Chunk cached = nmsWorld.getChunkIfLoaded(X, Z); - if (cached != null) return cached; - Future future = Fawe.get().getQueueHandler().sync((Supplier) () -> freshWorld.getChunkAt(X, Z)); - while (!future.isDone()) { - // this feels so dirty - freshWorld.getChunkProvider().runTasks(); - } - try { - return future.get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - } - }, null); - for (BlockVector3 vec : region) { - editSession.setBlock(vec, extent.getFullBlock(vec)); - } - } finally { - Fawe.get().getQueueHandler().endSet(true); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } catch (MaxChangedBlocksException e) { - throw new RuntimeException(e); - } finally { - saveFolder.delete(); - } - return true; + return false; //TODO: rework or remove for 1.16 } @Override From 82adab77b4693289a95d934db79532065d2df679 Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Sun, 8 Mar 2020 16:09:36 +1000 Subject: [PATCH 02/11] 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<>(); From b59b95c282caa93c5d8cfdb9fc518ee2c8b2de74 Mon Sep 17 00:00:00 2001 From: Octavia Togami Date: Sun, 22 Mar 2020 21:02:04 -0700 Subject: [PATCH 03/11] Cherry-pick WNA, minor changes. 1.16 VERY WIP First noticed incident of operations ruining ChunkSections. Do not build and use this unless you're testing. Rushed some of the changes, gotta sleep. Would be nice to get a review of this one from @mattbdev and @dordsor21 --- .../mc1_14/FAWEWorldNativeAccess_1_14.java | 176 +++++++++++++++++ .../FAWEWorldNativeAccess_1_15_2.java | 173 ++++++++++++++++ .../mc1_16_1/BlockMaterial_1_16_1.java | 2 +- .../mc1_16_1/FAWEWorldNativeAccess_1_16.java | 174 +++++++++++++++++ .../fawe/bukkit/wrapper/AsyncWorld.java | 28 ++- .../wrapper/state/AsyncDataContainer.java | 22 ++- .../sk89q/worldedit/bukkit/BukkitWorld.java | 50 ++--- .../bukkit/adapter/BukkitImplAdapter.java | 22 +-- .../adapter/IDelegateBukkitImplAdapter.java | 4 - .../adapter/impl/FAWE_Spigot_v1_14_R4.java | 29 +-- .../adapter/impl/FAWE_Spigot_v1_15_R2.java | 28 +-- .../adapter/impl/FAWE_Spigot_v1_16_R1.java | 28 +-- .../internal/wna/WorldNativeAccess.java | 184 ++++++++++++++++++ .../worldedit/internal/wna/package-info.java | 25 +++ .../com/sk89q/worldedit/util/SideEffect.java | 4 +- .../src/main/resources/lang/strings.json | 8 +- worldedit-fabric/build.gradle.kts | 4 +- .../sk89q/worldedit/fabric/FabricAdapter.java | 1 + .../worldedit/fabric/FabricDataFixer.java | 7 +- .../sk89q/worldedit/fabric/FabricEntity.java | 1 + .../worldedit/fabric/FabricPlatform.java | 2 +- .../sk89q/worldedit/fabric/FabricPlayer.java | 7 +- .../sk89q/worldedit/fabric/FabricWorld.java | 93 +-------- .../internal/FabricWorldNativeAccess.java | 139 +++++++++++++ .../fabric/{ => internal}/NBTConverter.java | 4 +- .../net/handler/WECUIPacketHandler.java | 8 +- .../sk89q/worldedit/forge/ForgeEntity.java | 1 + .../sk89q/worldedit/forge/ForgePlatform.java | 2 +- .../com/sk89q/worldedit/forge/ForgeWorld.java | 102 +--------- .../internal/ForgeWorldNativeAccess.java | 152 +++++++++++++++ .../forge/internal/TileEntityUtils.java | 35 +--- 31 files changed, 1170 insertions(+), 345 deletions(-) create mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/FAWEWorldNativeAccess_1_14.java create mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/FAWEWorldNativeAccess_1_15_2.java create mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/FAWEWorldNativeAccess_1_16.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/package-info.java create mode 100644 worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricWorldNativeAccess.java rename worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/{ => internal}/NBTConverter.java (99%) create mode 100644 worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeWorldNativeAccess.java diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/FAWEWorldNativeAccess_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/FAWEWorldNativeAccess_1_14.java new file mode 100644 index 000000000..9bcfc1ddd --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/FAWEWorldNativeAccess_1_14.java @@ -0,0 +1,176 @@ +package com.boydti.fawe.bukkit.adapter.mc1_14; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_14_R4; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import net.minecraft.server.v1_14_R1.Block; +import net.minecraft.server.v1_14_R1.BlockPosition; +import net.minecraft.server.v1_14_R1.Chunk; +import net.minecraft.server.v1_14_R1.ChunkProviderServer; +import net.minecraft.server.v1_14_R1.EnumDirection; +import net.minecraft.server.v1_14_R1.IBlockData; +import net.minecraft.server.v1_14_R1.NBTBase; +import net.minecraft.server.v1_14_R1.NBTTagCompound; +import net.minecraft.server.v1_14_R1.PlayerChunk; +import net.minecraft.server.v1_14_R1.TileEntity; +import net.minecraft.server.v1_14_R1.World; +import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_14_R1.block.data.CraftBlockData; +import org.bukkit.event.block.BlockPhysicsEvent; + +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; +import java.util.Objects; + +public class FAWEWorldNativeAccess_1_14 implements WorldNativeAccess { + private static final int UPDATE = 1, NOTIFY = 2; + + private final FAWE_Spigot_v1_14_R4 adapter; + private final WeakReference world; + private SideEffectSet sideEffectSet; + + public FAWEWorldNativeAccess_1_14(FAWE_Spigot_v1_14_R4 adapter, WeakReference world) { + this.adapter = adapter; + this.world = world; + } + + private World getWorld() { + return Objects.requireNonNull(world.get(), "The reference to the world was lost"); + } + + @Override + public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + + @Override + public Chunk getChunk(int x, int z) { + return getWorld().getChunkAt(x, z); + } + + @Override + public IBlockData toNative(com.sk89q.worldedit.world.block.BlockState state) { + int stateId = BlockStateIdAccess.getBlockStateId(state); + return BlockStateIdAccess.isValidInternalId(stateId) + ? Block.getByCombinedId(stateId) + : ((CraftBlockData) BukkitAdapter.adapt(state)).getState(); + } + + @Override + public IBlockData getBlockState(Chunk chunk, BlockPosition position) { + return chunk.getType(position); + } + + @Nullable + @Override + public IBlockData setBlockState(Chunk chunk, BlockPosition position, IBlockData state) { + return chunk.setType(position, state, false); + } + + @Override + public IBlockData getValidBlockForPosition(IBlockData block, BlockPosition position) { + return Block.b(block, getWorld(), position); + } + + @Override + public BlockPosition getPosition(int x, int y, int z) { + return new BlockPosition(x, y, z); + } + + @Override + public void updateLightingForBlock(BlockPosition position) { + getWorld().getChunkProvider().getLightEngine().a(position); + } + + @Override + public boolean updateTileEntity(BlockPosition position, CompoundTag tag) { + // We will assume that the tile entity was created for us, + // though we do not do this on the other versions + TileEntity tileEntity = getWorld().getTileEntity(position); + if (tileEntity == null) { + return false; + } + NBTBase nativeTag = adapter.fromNative(tag); + tileEntity.load((NBTTagCompound) nativeTag); + return true; + } + + @Override + public void notifyBlockUpdate(BlockPosition position, IBlockData oldState, IBlockData newState) { + getWorld().notify(position, oldState, newState, UPDATE | NOTIFY); + } + + @Override + public boolean isChunkTicking(Chunk chunk) { + return chunk.getState().isAtLeast(PlayerChunk.State.TICKING); + } + + @Override + public void markBlockChanged(BlockPosition position) { + ((ChunkProviderServer) getWorld().getChunkProvider()).flagDirty(position); + } + + private static final EnumDirection[] NEIGHBOUR_ORDER = { + EnumDirection.WEST, EnumDirection.EAST, + EnumDirection.DOWN, EnumDirection.UP, + EnumDirection.NORTH, EnumDirection.SOUTH + }; + + @Override + public void notifyNeighbors(BlockPosition pos, IBlockData oldState, IBlockData newState) { + World world = getWorld(); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + world.update(pos, oldState.getBlock()); + } else { + // When we don't want events, manually run the physics without them. + // Un-nest neighbour updating + for (EnumDirection direction : NEIGHBOUR_ORDER) { + BlockPosition shifted = pos.shift(direction); + world.getType(shifted).doPhysics(world, shifted, oldState.getBlock(), pos, false); + } + } + if (newState.isComplexRedstone()) { + world.updateAdjacentComparators(pos, newState.getBlock()); + } + } + + @Override + public void updateNeighbors(BlockPosition pos, IBlockData oldState, IBlockData newState, int recursionLimit) { + World world = getWorld(); + // a == updateNeighbors + // b == updateDiagonalNeighbors + oldState.b(world, pos, NOTIFY); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + CraftWorld craftWorld = world.getWorld(); + if (craftWorld != null) { + BlockPhysicsEvent event = new BlockPhysicsEvent( + craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), + CraftBlockData.fromData(newState)); + world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + } + } + newState.a(world, pos, NOTIFY); + newState.b(world, pos, NOTIFY); + } + + @Override + public void onBlockStateChange(BlockPosition pos, IBlockData oldState, IBlockData newState) { + getWorld().a(pos, oldState, newState); + } + + + @Override + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { + return this.adapter.setBlock(this.getChunk(position.getBlockX() >> 4, position.getBlockZ() >> 4).bukkitChunk, position.getBlockX(), position.getBlockY(), position.getBlockZ(), block, sideEffectSet.shouldApply(SideEffect.LIGHTING)); + } +} \ No newline at end of file diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/FAWEWorldNativeAccess_1_15_2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/FAWEWorldNativeAccess_1_15_2.java new file mode 100644 index 000000000..9ce24d789 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/FAWEWorldNativeAccess_1_15_2.java @@ -0,0 +1,173 @@ +package com.boydti.fawe.bukkit.adapter.mc1_15_2; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_15_R2; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import net.minecraft.server.v1_15_R1.Block; +import net.minecraft.server.v1_15_R1.BlockPosition; +import net.minecraft.server.v1_15_R1.Chunk; +import net.minecraft.server.v1_15_R1.ChunkProviderServer; +import net.minecraft.server.v1_15_R1.EnumDirection; +import net.minecraft.server.v1_15_R1.IBlockData; +import net.minecraft.server.v1_15_R1.NBTBase; +import net.minecraft.server.v1_15_R1.NBTTagCompound; +import net.minecraft.server.v1_15_R1.PlayerChunk; +import net.minecraft.server.v1_15_R1.TileEntity; +import net.minecraft.server.v1_15_R1.World; +import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_15_R1.block.data.CraftBlockData; +import org.bukkit.event.block.BlockPhysicsEvent; + +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; +import java.util.Objects; + +public class FAWEWorldNativeAccess_1_15_2 implements WorldNativeAccess { + private static final int UPDATE = 1, NOTIFY = 2; + + private final FAWE_Spigot_v1_15_R2 adapter; + private final WeakReference world; + private SideEffectSet sideEffectSet; + + public FAWEWorldNativeAccess_1_15_2(FAWE_Spigot_v1_15_R2 adapter, WeakReference world) { + this.adapter = adapter; + this.world = world; + } + + private World getWorld() { + return Objects.requireNonNull(world.get(), "The reference to the world was lost"); + } + + @Override + public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + + @Override + public Chunk getChunk(int x, int z) { + return getWorld().getChunkAt(x, z); + } + + @Override + public IBlockData toNative(com.sk89q.worldedit.world.block.BlockState state) { + int stateId = BlockStateIdAccess.getBlockStateId(state); + return BlockStateIdAccess.isValidInternalId(stateId) + ? Block.getByCombinedId(stateId) + : ((CraftBlockData) BukkitAdapter.adapt(state)).getState(); + } + + @Override + public IBlockData getBlockState(Chunk chunk, BlockPosition position) { + return chunk.getType(position); + } + + @Nullable + @Override + public IBlockData setBlockState(Chunk chunk, BlockPosition position, IBlockData state) { + return chunk.setType(position, state, false); + } + + @Override + public IBlockData getValidBlockForPosition(IBlockData block, BlockPosition position) { + return Block.b(block, getWorld(), position); + } + + @Override + public BlockPosition getPosition(int x, int y, int z) { + return new BlockPosition(x, y, z); + } + + @Override + public void updateLightingForBlock(BlockPosition position) { + getWorld().getChunkProvider().getLightEngine().a(position); + } + + @Override + public boolean updateTileEntity(BlockPosition position, CompoundTag tag) { + // We will assume that the tile entity was created for us, + // though we do not do this on the other versions + TileEntity tileEntity = getWorld().getTileEntity(position); + if (tileEntity == null) { + return false; + } + NBTBase nativeTag = adapter.fromNative(tag); + tileEntity.load((NBTTagCompound) nativeTag); + return true; + } + + @Override + public void notifyBlockUpdate(BlockPosition position, IBlockData oldState, IBlockData newState) { + getWorld().notify(position, oldState, newState, UPDATE | NOTIFY); + } + + @Override + public boolean isChunkTicking(Chunk chunk) { + return chunk.getState().isAtLeast(PlayerChunk.State.TICKING); + } + + @Override + public void markBlockChanged(BlockPosition position) { + ((ChunkProviderServer) getWorld().getChunkProvider()).flagDirty(position); + } + + private static final EnumDirection[] NEIGHBOUR_ORDER = { + EnumDirection.WEST, EnumDirection.EAST, + EnumDirection.DOWN, EnumDirection.UP, + EnumDirection.NORTH, EnumDirection.SOUTH + }; + + @Override + public void notifyNeighbors(BlockPosition pos, IBlockData oldState, IBlockData newState) { + World world = getWorld(); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + world.update(pos, oldState.getBlock()); + } else { + // When we don't want events, manually run the physics without them. + // Un-nest neighbour updating + for (EnumDirection direction : NEIGHBOUR_ORDER) { + BlockPosition shifted = pos.shift(direction); + world.getType(shifted).doPhysics(world, shifted, oldState.getBlock(), pos, false); + } + } + if (newState.isComplexRedstone()) { + world.updateAdjacentComparators(pos, newState.getBlock()); + } + } + + @Override + public void updateNeighbors(BlockPosition pos, IBlockData oldState, IBlockData newState, int recursionLimit) { + World world = getWorld(); + // a == updateNeighbors + // b == updateDiagonalNeighbors + oldState.b(world, pos, NOTIFY); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + CraftWorld craftWorld = world.getWorld(); + if (craftWorld != null) { + BlockPhysicsEvent event = new BlockPhysicsEvent(craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), CraftBlockData.fromData(newState)); + world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + } + } + newState.a(world, pos, NOTIFY); + newState.b(world, pos, NOTIFY); + } + + @Override + public void onBlockStateChange(BlockPosition pos, IBlockData oldState, IBlockData newState) { + getWorld().a(pos, oldState, newState); + } + + @Override + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { + return this.adapter.setBlock(this.getChunk(position.getBlockX() >> 4, position.getBlockZ() >> 4).bukkitChunk, position.getBlockX(), position.getBlockY(), position.getBlockZ(), block, sideEffectSet.shouldApply(SideEffect.LIGHTING)); + } +} \ No newline at end of file diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BlockMaterial_1_16_1.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BlockMaterial_1_16_1.java index ef492dbdd..dfa348a78 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BlockMaterial_1_16_1.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BlockMaterial_1_16_1.java @@ -23,7 +23,7 @@ public class BlockMaterial_1_16_1 implements BlockMaterial { this.material = defaultState.getMaterial(); this.craftBlockData = CraftBlockData.fromData(defaultState); this.craftMaterial = craftBlockData.getMaterial(); - this.isTranslucent = !(boolean) ReflectionUtil.getField(Block.class, block, "v"); //TODO Update Mapping for 1.16.1 + this.isTranslucent = !(boolean) ReflectionUtil.getField(Block.class, block, "at"); //TODO Update Mapping for 1.16.1 } public Block getBlock() { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/FAWEWorldNativeAccess_1_16.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/FAWEWorldNativeAccess_1_16.java new file mode 100644 index 000000000..6b68ae95d --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/FAWEWorldNativeAccess_1_16.java @@ -0,0 +1,174 @@ +package com.boydti.fawe.bukkit.adapter.mc1_16_1; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_16_R1; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import net.minecraft.server.v1_16_R1.Block; +import net.minecraft.server.v1_16_R1.BlockPosition; +import net.minecraft.server.v1_16_R1.Chunk; +import net.minecraft.server.v1_16_R1.ChunkProviderServer; +import net.minecraft.server.v1_16_R1.EnumDirection; +import net.minecraft.server.v1_16_R1.IBlockData; +import net.minecraft.server.v1_16_R1.NBTBase; +import net.minecraft.server.v1_16_R1.NBTTagCompound; +import net.minecraft.server.v1_16_R1.PlayerChunk; +import net.minecraft.server.v1_16_R1.TileEntity; +import net.minecraft.server.v1_16_R1.World; +import org.bukkit.craftbukkit.v1_16_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R1.block.data.CraftBlockData; +import org.bukkit.event.block.BlockPhysicsEvent; + +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; +import java.util.Objects; + +public class FAWEWorldNativeAccess_1_16 implements WorldNativeAccess { + private static final int UPDATE = 1, NOTIFY = 2; + + private final FAWE_Spigot_v1_16_R1 adapter; + private final WeakReference world; + private SideEffectSet sideEffectSet; + + public FAWEWorldNativeAccess_1_16(FAWE_Spigot_v1_16_R1 adapter, WeakReference world) { + this.adapter = adapter; + this.world = world; + } + + private World getWorld() { + return Objects.requireNonNull(world.get(), "The reference to the world was lost"); + } + + @Override + public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + + @Override + public Chunk getChunk(int x, int z) { + return getWorld().getChunkAt(x, z); + } + + @Override + public IBlockData toNative(BlockState state) { + int stateId = BlockStateIdAccess.getBlockStateId(state); + return BlockStateIdAccess.isValidInternalId(stateId) + ? Block.getByCombinedId(stateId) + : ((CraftBlockData) BukkitAdapter.adapt(state)).getState(); + } + + @Override + public IBlockData getBlockState(Chunk chunk, BlockPosition position) { + return chunk.getType(position); + } + + @Nullable + @Override + public IBlockData setBlockState(Chunk chunk, BlockPosition position, IBlockData state) { + return chunk.setType(position, state, false); + } + + @Override + public IBlockData getValidBlockForPosition(IBlockData block, BlockPosition position) { + return Block.b(block, getWorld(), position); + } + + @Override + public BlockPosition getPosition(int x, int y, int z) { + return new BlockPosition(x, y, z); + } + + @Override + public void updateLightingForBlock(BlockPosition position) { + getWorld().getChunkProvider().getLightEngine().a(position); + } + + @Override + public boolean updateTileEntity(BlockPosition position, CompoundTag tag) { + // We will assume that the tile entity was created for us, + // though we do not do this on the other versions + TileEntity tileEntity = getWorld().getTileEntity(position); + if (tileEntity == null) { + return false; + } + NBTBase nativeTag = adapter.fromNative(tag); + tileEntity.load(tileEntity.getBlock(), (NBTTagCompound) nativeTag); + return true; + } + + @Override + public void notifyBlockUpdate(BlockPosition position, IBlockData oldState, IBlockData newState) { + getWorld().notify(position, oldState, newState, UPDATE | NOTIFY); + } + + @Override + public boolean isChunkTicking(Chunk chunk) { + return chunk.getState().isAtLeast(PlayerChunk.State.TICKING); + } + + @Override + public void markBlockChanged(BlockPosition position) { + ((ChunkProviderServer) getWorld().getChunkProvider()).flagDirty(position); + } + + private static final EnumDirection[] NEIGHBOUR_ORDER = { + EnumDirection.WEST, EnumDirection.EAST, + EnumDirection.DOWN, EnumDirection.UP, + EnumDirection.NORTH, EnumDirection.SOUTH + }; + + @Override + public void notifyNeighbors(BlockPosition pos, IBlockData oldState, IBlockData newState) { + World world = getWorld(); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + world.update(pos, oldState.getBlock()); + } else { + // When we don't want events, manually run the physics without them. + // Un-nest neighbour updating + for (EnumDirection direction : NEIGHBOUR_ORDER) { + BlockPosition shifted = pos.shift(direction); + world.getType(shifted).doPhysics(world, shifted, oldState.getBlock(), pos, false); + } + } + if (newState.isComplexRedstone()) { + world.updateAdjacentComparators(pos, newState.getBlock()); + } + } + + @Override + public void updateNeighbors(BlockPosition pos, IBlockData oldState, IBlockData newState, int recursionLimit) { + World world = getWorld(); + // a == updateNeighbors + // b == updateDiagonalNeighbors + oldState.b(world, pos, NOTIFY, recursionLimit); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + CraftWorld craftWorld = world.getWorld(); + if (craftWorld != null) { + BlockPhysicsEvent event = new BlockPhysicsEvent(craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), CraftBlockData.fromData(newState)); + world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + } + } + newState.a(world, pos, NOTIFY, recursionLimit); + newState.b(world, pos, NOTIFY, recursionLimit); + } + + @Override + public void onBlockStateChange(BlockPosition pos, IBlockData oldState, IBlockData newState) { + getWorld().a(pos, oldState, newState); + } + + @Override + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { + return this.adapter.setBlock(this.getChunk(position.getBlockX() >> 4, position.getBlockZ() >> 4).bukkitChunk, position.getBlockX(), position.getBlockY(), position.getBlockZ(), block, sideEffectSet.shouldApply(SideEffect.LIGHTING)); + } +} \ No newline at end of file diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java index 4252d3f32..60e0f8037 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java @@ -884,6 +884,16 @@ public class AsyncWorld extends PassthroughExtent implements World { parent.setWaterAnimalSpawnLimit(limit); } + @Override + public int getWaterAmbientSpawnLimit() { + return 0; + } + + @Override + public void setWaterAmbientSpawnLimit(int limit) { + + } + @Override public int getAmbientSpawnLimit() { return parent.getAmbientSpawnLimit(); @@ -1285,18 +1295,28 @@ public class AsyncWorld extends PassthroughExtent implements World { } public long getTicksPerWaterSpawns() { - throw new UnsupportedOperationException(); + return parent.getTicksPerWaterSpawns(); } public void setTicksPerWaterSpawns(int ticksPerWaterSpawns) { - throw new UnsupportedOperationException(); + parent.setTicksPerWaterSpawns(ticksPerWaterSpawns); + } + + @Override + public long getTicksPerWaterAmbientSpawns() { + return parent.getTicksPerWaterAmbientSpawns(); + } + + @Override + public void setTicksPerWaterAmbientSpawns(int ticksPerAmbientSpawns) { + parent.setTicksPerWaterAmbientSpawns(ticksPerAmbientSpawns); } public long getTicksPerAmbientSpawns() { - throw new UnsupportedOperationException(); + return parent.getTicksPerAmbientSpawns(); } public void setTicksPerAmbientSpawns(int ticksPerAmbientSpawns) { - throw new UnsupportedOperationException(); + parent.setTicksPerAmbientSpawns(ticksPerAmbientSpawns); } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java index f1fbbcbb2..e64db1124 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java @@ -3,15 +3,15 @@ package com.boydti.fawe.bukkit.wrapper.state; import com.boydti.fawe.FaweCache; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; + +import java.util.*; + import org.apache.commons.lang.Validate; import org.bukkit.NamespacedKey; import org.bukkit.persistence.PersistentDataAdapterContext; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.NotNull; public final class AsyncDataContainer implements PersistentDataContainer { private final CompoundTag root; @@ -69,6 +69,20 @@ public final class AsyncDataContainer implements PersistentDataContainer { return z != null ? z : defaultValue; } + @NotNull + @Override + public Set getKeys() { + Set keys = new HashSet<>(); + this.get(false).keySet().forEach(key -> { + String[] keyData = key.split(":", 2); + if (keyData.length == 2) { + keys.add(new NamespacedKey(keyData[0], keyData[1])); + } + + }); + return keys; + } + public void remove(NamespacedKey key) { Validate.notNull(key, "The provided key for the custom value was null"); get(false).remove(key.toString()); 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 f11fc13d3..588071c34 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 @@ -24,8 +24,9 @@ import static com.google.common.base.Preconditions.checkNotNull; 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.ImmutableSet; import com.google.common.collect.Sets; +import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; @@ -34,6 +35,7 @@ import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; @@ -77,6 +79,7 @@ public class BukkitWorld extends AbstractWorld { } private final WeakReference worldRef; + private final WorldNativeAccess worldNativeAccess; /** * Construct the object. @@ -85,6 +88,12 @@ public class BukkitWorld extends AbstractWorld { */ public BukkitWorld(World world) { this.worldRef = new WeakReference<>(world); + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + if (adapter != null) { + this.worldNativeAccess = adapter.createWorldNativeAccess(world); + } else { + this.worldNativeAccess = null; + } } @Override @@ -464,26 +473,22 @@ public class BukkitWorld extends AbstractWorld { } @Override - public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { - BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - if (adapter != null) { + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) { + if (worldNativeAccess != null) { try { - return adapter.setBlock(BukkitAdapter.adapt(getWorld(), position), block, sideEffects); + return worldNativeAccess.setBlock(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()); - logger.warn(((BaseBlock) block).getNbtData().toString()); + logger.warn("Tried to set a corrupt tile entity at " + position.toString() + + ": " + ((BaseBlock) block).getNbtData(), e); + } else { + logger.warn("Failed to set block via adapter, falling back to generic", e); } - e.printStackTrace(); - Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()); - 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), sideEffects.doesApplyAny()); - return true; } + Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()); + bukkitBlock.setBlockData(BukkitAdapter.adapt(block), sideEffects.doesApplyAny()); + return true; } @Override @@ -497,20 +502,17 @@ public class BukkitWorld extends AbstractWorld { } @Override - 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.applySideEffects(BukkitAdapter.adapt(getWorld(), position), previousType, sideEffectSet); + public Set applySideEffects(BlockVector3 position, com.sk89q.worldedit.world.block.BlockState previousType, + SideEffectSet sideEffectSet) { + if (worldNativeAccess != null) { + worldNativeAccess.applySideEffects(position, previousType, sideEffectSet); return Sets.intersection( - adapter.getSupportedSideEffects(), + WorldEditPlugin.getInstance().getInternalPlatform().getSupportedSideEffects(), sideEffectSet.getSideEffectsToApply() ); } - return Sets.intersection( - WorldEditPlugin.getInstance().getInternalPlatform().getSupportedSideEffects(), - sideEffectSet.getSideEffectsToApply() - ); + return ImmutableSet.of(); } @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 416b4b621..d77073045 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 @@ -30,17 +30,16 @@ import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; 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; 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.registry.BlockMaterial; import java.util.Map; @@ -98,23 +97,12 @@ public interface BukkitImplAdapter extends IBukkitAdapter { BaseBlock getBlock(Location location); /** - * Set the block at the given location. + * Create a {@link WorldNativeAccess} for the given world reference. * - * @param location the location - * @param state the block - * @param sideEffectSet side effects to apply - * @return true if a block was likely changed + * @param world the world reference + * @return the native access object */ - boolean setBlock(Location location, BlockStateHolder state, SideEffectSet sideEffectSet); - - /** - * 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 applySideEffects(Location position, BlockState previousType, SideEffectSet sideEffectSet); + WorldNativeAccess createWorldNativeAccess(World world); /** * Get the state for the given entity. 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 9f5499dcd..5bf4d88af 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 @@ -66,10 +66,6 @@ public interface IDelegateBukkitImplAdapter extends BukkitImplAdapter { return getParent().getBlock(location); } - default > boolean setBlock(Location location, B state, boolean notifyAndLight) { - return getParent().setBlock(location, state, SideEffectSet.none()); - } - @Override @Nullable default BaseEntity getEntity(Entity entity) { 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 21bcf1718..09aa43ca3 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 @@ -24,10 +24,7 @@ import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.boydti.fawe.beta.implementation.queue.SingleThreadQueueExtent; -import com.boydti.fawe.bukkit.adapter.mc1_14.BlockMaterial_1_14; -import com.boydti.fawe.bukkit.adapter.mc1_14.BukkitAdapter_1_14; -import com.boydti.fawe.bukkit.adapter.mc1_14.BukkitGetBlocks_1_14; -import com.boydti.fawe.bukkit.adapter.mc1_14.MapChunkUtil_1_14; +import com.boydti.fawe.bukkit.adapter.mc1_14.*; import com.boydti.fawe.bukkit.adapter.mc1_14.nbt.LazyCompoundTag_1_14; import com.google.common.io.Files; import com.sk89q.jnbt.CompoundTag; @@ -42,13 +39,13 @@ import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.LazyBaseEntity; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; 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; import com.sk89q.worldedit.world.block.*; import com.sk89q.worldedit.world.entity.EntityType; @@ -71,6 +68,7 @@ import org.bukkit.generator.ChunkGenerator; import javax.annotation.Nullable; import java.io.File; import java.io.IOException; +import java.lang.ref.WeakReference; import java.util.Map; import java.util.OptionalInt; import java.util.Set; @@ -151,26 +149,11 @@ 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); - } - 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(); @@ -222,6 +205,12 @@ public final class FAWE_Spigot_v1_14_R4 extends CachedBukkitAdapter implements I return true; } + @Override + public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { + return new FAWEWorldNativeAccess_1_14(this, + new WeakReference<>(((CraftWorld) world).getHandle())); + } + @Nullable private static String getEntityId(Entity entity) { MinecraftKey minecraftkey = EntityTypes.getName(entity.getEntityType()); 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 ffbe28f1d..85dd26089 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 @@ -26,10 +26,7 @@ import com.boydti.fawe.beta.IQueueChunk; import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.boydti.fawe.beta.implementation.queue.SingleThreadQueueExtent; -import com.boydti.fawe.bukkit.adapter.mc1_15_2.BlockMaterial_1_15_2; -import com.boydti.fawe.bukkit.adapter.mc1_15_2.BukkitAdapter_1_15_2; -import com.boydti.fawe.bukkit.adapter.mc1_15_2.BukkitGetBlocks_1_15_2; -import com.boydti.fawe.bukkit.adapter.mc1_15_2.MapChunkUtil_1_15_2; +import com.boydti.fawe.bukkit.adapter.mc1_15_2.*; import com.boydti.fawe.bukkit.adapter.mc1_15_2.nbt.LazyCompoundTag_1_15_2; import com.google.common.io.Files; import com.sk89q.jnbt.CompoundTag; @@ -44,6 +41,7 @@ import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.LazyBaseEntity; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; @@ -73,6 +71,7 @@ import org.bukkit.generator.ChunkGenerator; import javax.annotation.Nullable; import java.io.File; import java.io.IOException; +import java.lang.ref.WeakReference; import java.util.Map; import java.util.OptionalInt; import java.util.Set; @@ -160,26 +159,11 @@ 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, BlockStateHolder state, boolean update) { CraftChunk craftChunk = (CraftChunk) chunk; Chunk nmsChunk = craftChunk.getHandle(); @@ -231,6 +215,12 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I return true; } + @Override + public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { + return new FAWEWorldNativeAccess_1_15_2(this, + new WeakReference<>(((CraftWorld) world).getHandle())); + } + @Nullable private static String getEntityId(Entity entity) { MinecraftKey minecraftkey = EntityTypes.getName(entity.getEntityType()); 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 a97372095..3a5f2af11 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 @@ -26,10 +26,7 @@ import com.boydti.fawe.beta.IQueueChunk; import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.boydti.fawe.beta.implementation.queue.SingleThreadQueueExtent; -import com.boydti.fawe.bukkit.adapter.mc1_16_1.BlockMaterial_1_16_1; -import com.boydti.fawe.bukkit.adapter.mc1_16_1.BukkitAdapter_1_16_1; -import com.boydti.fawe.bukkit.adapter.mc1_16_1.BukkitGetBlocks_1_16_1; -import com.boydti.fawe.bukkit.adapter.mc1_16_1.MapChunkUtil_1_16_1; +import com.boydti.fawe.bukkit.adapter.mc1_16_1.*; import com.boydti.fawe.bukkit.adapter.mc1_16_1.nbt.LazyCompoundTag_1_16_1; import com.google.common.io.Files; import com.sk89q.jnbt.CompoundTag; @@ -44,6 +41,7 @@ import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.LazyBaseEntity; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; @@ -72,6 +70,7 @@ import org.bukkit.generator.ChunkGenerator; import javax.annotation.Nullable; import java.io.File; import java.io.IOException; +import java.lang.ref.WeakReference; import java.util.Map; import java.util.OptionalInt; import java.util.Set; @@ -159,26 +158,11 @@ 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, BlockStateHolder state, boolean update) { CraftChunk craftChunk = (CraftChunk) chunk; Chunk nmsChunk = craftChunk.getHandle(); @@ -230,6 +214,12 @@ public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements I return true; } + @Override + public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { + return new FAWEWorldNativeAccess_1_16(this, + new WeakReference<>(((CraftWorld)world).getHandle())); + } + @Nullable private static String getEntityId(Entity entity) { MinecraftKey minecraftkey = EntityTypes.getName(entity.getEntityType()); 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 new file mode 100644 index 000000000..ac32171e7 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java @@ -0,0 +1,184 @@ +/* + * 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.internal.wna; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Natively access and perform operations on the world. + * + * @param the native chunk type + * @param the native block state type + * @param the native position type + */ +public interface WorldNativeAccess { + + default > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { + checkNotNull(position); + checkNotNull(block); + setCurrentSideEffectSet(sideEffects); + + int x = position.getBlockX(); + int y = position.getBlockY(); + int z = position.getBlockZ(); + + // First set the block + NC chunk = getChunk(x >> 4, z >> 4); + NP pos = getPosition(x, y, z); + NBS old = getBlockState(chunk, pos); + NBS newState = toNative(block.toImmutableState()); + // change block prior to placing if it should be fixed + if (sideEffects.shouldApply(SideEffect.VALIDATION)) { + newState = getValidBlockForPosition(newState, pos); + } + NBS successState = setBlockState(chunk, pos, newState); + boolean successful = successState != null; + + // Create the TileEntity + if (successful || old == newState) { + if (block instanceof BaseBlock) { + BaseBlock baseBlock = (BaseBlock) block; + CompoundTag tag = baseBlock.getNbtData(); + if (tag != null) { + tag = tag.createBuilder() + .putString("id", baseBlock.getNbtId()) + .putInt("x", position.getX()) + .putInt("y", position.getY()) + .putInt("z", position.getZ()) + .build(); + // update if TE changed as well + successful = updateTileEntity(pos, tag); + } + } + } + + if (successful) { + if (sideEffects.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { + updateLightingForBlock(pos); + } + markAndNotifyBlock(pos, chunk, old, newState, sideEffects); + } + + return successful; + } + + default void applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) { + setCurrentSideEffectSet(sideEffectSet); + NP pos = getPosition(position.getX(), position.getY(), position.getZ()); + NC chunk = getChunk(position.getX() >> 4, position.getZ() >> 4); + NBS oldData = toNative(previousType); + NBS newData = getBlockState(chunk, pos); + + if (sideEffectSet.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { + updateLightingForBlock(pos); + } + + markAndNotifyBlock(pos, chunk, oldData, newData, sideEffectSet); + } + + // state-keeping functions for WNA + // may be thread-unsafe, as this is single-threaded code + + /** + * Receive the current side-effect set from the high level call. + * + * This allows the implementation to branch on the side-effects internally. + * + * @param sideEffectSet the set of side-effects + */ + default void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { + } + + // access functions + + NC getChunk(int x, int z); + + NBS toNative(BlockState state); + + NBS getBlockState(NC chunk, NP position); + + @Nullable + NBS setBlockState(NC chunk, NP position, NBS state); + + NBS getValidBlockForPosition(NBS block, NP position); + + NP getPosition(int x, int y, int z); + + void updateLightingForBlock(NP position); + + boolean updateTileEntity(NP position, CompoundTag tag); + + void notifyBlockUpdate(NP position, NBS oldState, NBS newState); + + boolean isChunkTicking(NC chunk); + + void markBlockChanged(NP position); + + void notifyNeighbors(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(pos, oldState, newState); + } else { + // If we want to skip entity AI, just mark the block for sending + markBlockChanged(pos); + } + } + + if (sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) { + notifyNeighbors(pos, oldState, newState); + } + + // Make connection updates optional + if (sideEffectSet.shouldApply(SideEffect.VALIDATION)) { + updateNeighbors(pos, oldState, newState, 512); + } + + onBlockStateChange(pos, oldState, newState); + } + +} 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 new file mode 100644 index 000000000..69009eb59 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/package-info.java @@ -0,0 +1,25 @@ +/* + * 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 . + */ + +/** + * "WNA", or WorldEdit Native Access. + * + * Contains internal helper functions for sharing code between platforms. + */ +package com.sk89q.worldedit.internal.wna; 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 index c4c814fcb..5b142a364 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffect.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffect.java @@ -24,9 +24,9 @@ import java.util.Locale; public enum SideEffect { LIGHTING(State.ON), NEIGHBORS(State.ON), - CONNECTIONS(State.ON), + VALIDATION(State.ON), ENTITY_AI(State.OFF), - PLUGIN_EVENTS(State.OFF); + EVENTS(State.OFF); private final String displayName; private final String description; diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index 4708d4596..016a3d60d 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -502,12 +502,12 @@ "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.validation": "Validation", + "worldedit.sideeffect.validation.description": "Validates and fixes inconsistent world state, such as disconnected blocks", "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.events": "Mod/Plugin Events", + "worldedit.sideeffect.events.description": "Tells other mods/plugins about these changes when applicable", "worldedit.sideeffect.state.on": "On", "worldedit.sideeffect.state.delayed": "Delayed", "worldedit.sideeffect.state.off": "Off", diff --git a/worldedit-fabric/build.gradle.kts b/worldedit-fabric/build.gradle.kts index 1cf6df8dd..4b7a7b079 100644 --- a/worldedit-fabric/build.gradle.kts +++ b/worldedit-fabric/build.gradle.kts @@ -7,8 +7,8 @@ applyShadowConfiguration() apply(plugin = "fabric-loom") val minecraftVersion = "1.15.2" -val yarnMappings = "1.15.2+build.8:v2" -val loaderVersion = "0.7.6+build.180" +val yarnMappings = "1.15.2+build.14:v2" +val loaderVersion = "0.7.8+build.189" configurations.all { resolutionStrategy { diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java index 317274331..33219c7a3 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java @@ -25,6 +25,7 @@ import com.google.common.collect.ImmutableList; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.fabric.internal.NBTConverter; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.registry.state.BooleanProperty; diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricDataFixer.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricDataFixer.java index f307b789c..d82bb8f75 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricDataFixer.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricDataFixer.java @@ -36,9 +36,10 @@ import com.mojang.datafixers.DataFixerBuilder; import com.mojang.datafixers.Dynamic; import com.mojang.datafixers.schemas.Schema; import com.sk89q.jnbt.CompoundTag; -import net.minecraft.datafixers.NbtOps; -import net.minecraft.datafixers.Schemas; -import net.minecraft.datafixers.TypeReferences; +import com.sk89q.worldedit.fabric.internal.NBTConverter; +import net.minecraft.datafixer.NbtOps; +import net.minecraft.datafixer.Schemas; +import net.minecraft.datafixer.TypeReferences; import net.minecraft.nbt.FloatTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.StringTag; diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java index d1fb75a04..4ea3969fd 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.metadata.EntityProperties; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.fabric.internal.NBTConverter; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.NullWorld; 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 7fb18b0e7..bef543f6e 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 @@ -202,7 +202,7 @@ class FabricPlatform extends AbstractPlatform implements MultiUserPlatform { } private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( - SideEffect.CONNECTIONS, + SideEffect.VALIDATION, SideEffect.ENTITY_AI, SideEffect.LIGHTING, SideEffect.NEIGHBORS diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlayer.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlayer.java index 48507f8fe..1ee00d311 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlayer.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlayer.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extension.platform.AbstractPlayerActor; import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.fabric.internal.NBTConverter; import com.sk89q.worldedit.fabric.mixin.AccessorServerPlayerEntity; import com.sk89q.worldedit.fabric.net.handler.WECUIPacketHandler; import com.sk89q.worldedit.internal.cui.CUIEvent; @@ -42,10 +43,10 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; import io.netty.buffer.Unpooled; import net.minecraft.block.Block; -import net.minecraft.client.network.packet.BlockEntityUpdateS2CPacket; -import net.minecraft.client.network.packet.BlockUpdateS2CPacket; -import net.minecraft.client.network.packet.CustomPayloadS2CPacket; import net.minecraft.item.ItemStack; +import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket; +import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket; +import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.LiteralText; import net.minecraft.text.Text; 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 e085d2326..93e05a820 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 @@ -34,6 +34,8 @@ import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.fabric.internal.FabricWorldNativeAccess; +import com.sk89q.worldedit.fabric.internal.NBTConverter; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; import com.sk89q.worldedit.math.BlockVector2; @@ -64,7 +66,6 @@ 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; @@ -129,6 +130,7 @@ public class FabricWorld extends AbstractWorld { private static final net.minecraft.block.BlockState JUNGLE_SHRUB = Blocks.OAK_LEAVES.getDefaultState().with(LeavesBlock.PERSISTENT, Boolean.TRUE); private final WeakReference worldRef; + private final FabricWorldNativeAccess worldNativeAccess; /** * Construct a new world. @@ -138,6 +140,7 @@ public class FabricWorld extends AbstractWorld { FabricWorld(World world) { checkNotNull(world); this.worldRef = new WeakReference<>(world); + this.worldNativeAccess = new FabricWorldNativeAccess(worldRef); } /** @@ -191,98 +194,14 @@ 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, SideEffectSet sideEffects) throws WorldEditException { - checkNotNull(position); - checkNotNull(block); - - World world = getWorldChecked(); - int x = position.getBlockX(); - int y = position.getBlockY(); - int z = position.getBlockZ(); - - // First set the block - 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()); - net.minecraft.block.BlockState newState = stateId.isPresent() ? Block.getStateFromRawId(stateId.getAsInt()) : FabricAdapter.adapt(block.toImmutableState()); - net.minecraft.block.BlockState successState = chunk.setBlockState(pos, newState, false); - boolean successful = successState != null; - - // Create the TileEntity - if (successful || old == newState) { - if (block instanceof BaseBlock) { - CompoundTag tag = ((BaseBlock) block).getNbtData(); - if (tag != null) { - net.minecraft.nbt.CompoundTag nativeTag = NBTConverter.toNative(tag); - BlockEntity tileEntity = getWorld().getWorldChunk(pos).getBlockEntity(pos); - if (tileEntity != null) { - tileEntity.fromTag(nativeTag); - tileEntity.setPos(pos); - tileEntity.setWorld(world); - successful = true; // update if TE changed as well - } - } - } - } - - if (successful) { - if (sideEffects.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { - world.getChunkManager().getLightingProvider().checkBlock(pos); - } - markAndNotifyBlock(world, pos, chunk, old, newState, sideEffects); - } - - return successful; + return worldNativeAccess.setBlock(position, block, sideEffects); } @Override 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 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 + worldNativeAccess.applySideEffects(position, previousType, sideEffectSet); return Sets.intersection(FabricWorldEdit.inst.getPlatform().getSupportedSideEffects(), sideEffectSet.getSideEffectsToApply()); } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricWorldNativeAccess.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricWorldNativeAccess.java new file mode 100644 index 000000000..6d64e39de --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricWorldNativeAccess.java @@ -0,0 +1,139 @@ +/* + * 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.fabric.internal; + +import com.sk89q.worldedit.fabric.FabricAdapter; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.world.ChunkHolder; +import net.minecraft.server.world.ServerChunkManager; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.chunk.WorldChunk; + +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; +import java.util.Objects; + +public class FabricWorldNativeAccess implements WorldNativeAccess { + private static final int UPDATE = 1, NOTIFY = 2; + + private final WeakReference world; + + public FabricWorldNativeAccess(WeakReference world) { + this.world = world; + } + + private World getWorld() { + return Objects.requireNonNull(world.get(), "The reference to the world was lost"); + } + + @Override + public WorldChunk getChunk(int x, int z) { + return getWorld().getChunk(x, z); + } + + @Override + public BlockState toNative(com.sk89q.worldedit.world.block.BlockState state) { + int stateId = BlockStateIdAccess.getBlockStateId(state); + return BlockStateIdAccess.isValidInternalId(stateId) + ? Block.getStateFromRawId(stateId) + : FabricAdapter.adapt(state); + } + + @Override + public BlockState getBlockState(WorldChunk chunk, BlockPos position) { + return chunk.getBlockState(position); + } + + @Nullable + @Override + public BlockState setBlockState(WorldChunk chunk, BlockPos position, BlockState state) { + return chunk.setBlockState(position, state, false); + } + + @Override + public BlockState getValidBlockForPosition(BlockState block, BlockPos position) { + return Block.getRenderingState(block, getWorld(), position); + } + + @Override + public BlockPos getPosition(int x, int y, int z) { + return new BlockPos(x, y, z); + } + + @Override + public void updateLightingForBlock(BlockPos position) { + getWorld().getChunkManager().getLightingProvider().checkBlock(position); + } + + @Override + public boolean updateTileEntity(BlockPos position, com.sk89q.jnbt.CompoundTag tag) { + CompoundTag nativeTag = NBTConverter.toNative(tag); + BlockEntity tileEntity = getWorld().getWorldChunk(position).getBlockEntity(position); + if (tileEntity == null) { + return false; + } + tileEntity.setLocation(getWorld(), position); + tileEntity.fromTag(nativeTag); + return true; + } + + @Override + public void notifyBlockUpdate(BlockPos position, BlockState oldState, BlockState newState) { + getWorld().updateListeners(position, oldState, newState, UPDATE | NOTIFY); + } + + @Override + public boolean isChunkTicking(WorldChunk chunk) { + return chunk.getLevelType().isAfter(ChunkHolder.LevelType.TICKING); + } + + @Override + public void markBlockChanged(BlockPos position) { + ((ServerChunkManager) getWorld().getChunkManager()).markForUpdate(position); + } + + @Override + public void notifyNeighbors(BlockPos pos, BlockState oldState, BlockState newState) { + getWorld().updateNeighbors(pos, oldState.getBlock()); + if (newState.hasComparatorOutput()) { + getWorld().updateHorizontalAdjacent(pos, newState.getBlock()); + } + } + + @Override + public void updateNeighbors(BlockPos pos, BlockState oldState, BlockState newState) { + World world = getWorld(); + // method_11637 = updateDiagonalNeighbors + oldState.method_11637(world, pos, NOTIFY); + newState.updateNeighborStates(world, pos, NOTIFY); + newState.method_11637(world, pos, NOTIFY); + } + + @Override + public void onBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) { + getWorld().onBlockChanged(pos, oldState, newState); + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/NBTConverter.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/NBTConverter.java similarity index 99% rename from worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/NBTConverter.java rename to worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/NBTConverter.java index 450dfcb65..2cdc6be4e 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/NBTConverter.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/NBTConverter.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.fabric; +package com.sk89q.worldedit.fabric.internal; import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.ByteTag; @@ -44,7 +44,7 @@ import java.util.Set; /** * Converts between JNBT and Minecraft NBT classes. */ -final class NBTConverter { +public final class NBTConverter { private NBTConverter() { } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/net/handler/WECUIPacketHandler.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/net/handler/WECUIPacketHandler.java index 816d6c76f..4571e50d2 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/net/handler/WECUIPacketHandler.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/net/handler/WECUIPacketHandler.java @@ -23,15 +23,9 @@ import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.fabric.FabricAdapter; import com.sk89q.worldedit.fabric.FabricPlayer; import com.sk89q.worldedit.fabric.FabricWorldEdit; -import net.fabricmc.fabric.api.network.PacketConsumer; -import net.fabricmc.fabric.api.network.PacketContext; import net.fabricmc.fabric.api.network.ServerSidePacketRegistry; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.packet.CustomPayloadS2CPacket; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.network.packet.CustomPayloadC2SPacket; import net.minecraft.util.Identifier; -import net.minecraft.util.PacketByteBuf; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -58,4 +52,4 @@ public final class WECUIPacketHandler { session.describeCUI(actor); }); } -} \ No newline at end of file +} diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeEntity.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeEntity.java index a7f99e1a7..d522107d6 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeEntity.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeEntity.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.metadata.EntityProperties; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.forge.internal.NBTConverter; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.NullWorld; 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 b844425eb..7c682b81d 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 @@ -208,7 +208,7 @@ class ForgePlatform extends AbstractPlatform implements MultiUserPlatform { } private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( - SideEffect.CONNECTIONS, + SideEffect.VALIDATION, SideEffect.ENTITY_AI, SideEffect.LIGHTING, SideEffect.NEIGHBORS 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 5d69754a6..4aa3b7a92 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,8 +19,6 @@ 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; @@ -34,6 +32,9 @@ import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.forge.internal.ForgeWorldNativeAccess; +import com.sk89q.worldedit.forge.internal.NBTConverter; +import com.sk89q.worldedit.forge.internal.TileEntityUtils; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; import com.sk89q.worldedit.internal.util.BiomeMath; @@ -105,6 +106,7 @@ 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; @@ -119,7 +121,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; -import javax.annotation.Nullable; +import static com.google.common.base.Preconditions.checkNotNull; /** * An adapter to Minecraft worlds for WorldEdit. @@ -127,13 +129,13 @@ import javax.annotation.Nullable; public class ForgeWorld extends AbstractWorld { private static final Random random = new Random(); - private static final int UPDATE = 1, NOTIFY = 2; 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; + private final ForgeWorldNativeAccess nativeAccess; /** * Construct a new world. @@ -143,6 +145,7 @@ public class ForgeWorld extends AbstractWorld { ForgeWorld(World world) { checkNotNull(world); this.worldRef = new WeakReference<>(world); + this.nativeAccess = new ForgeWorldNativeAccess(worldRef); } /** @@ -194,101 +197,14 @@ 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, SideEffectSet sideEffects) throws WorldEditException { - checkNotNull(position); - checkNotNull(block); - - World world = getWorldChecked(); - int x = position.getBlockX(); - int y = position.getBlockY(); - int z = position.getBlockZ(); - - // First set the block - Chunk 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()); - net.minecraft.block.BlockState newState = stateId.isPresent() ? Block.getStateById(stateId.getAsInt()) : ForgeAdapter.adapt(block.toImmutableState()); - net.minecraft.block.BlockState successState = chunk.setBlockState(pos, newState, false); - boolean successful = successState != null; - - // Create the TileEntity - if (successful || old == newState) { - if (block instanceof BaseBlock) { - CompoundTag tag = ((BaseBlock) block).getNbtData(); - if (tag != null) { - CompoundNBT nativeTag = NBTConverter.toNative(tag); - nativeTag.putString("id", ((BaseBlock) block).getNbtId()); - TileEntityUtils.setTileEntity(world, position, nativeTag); - successful = true; // update if TE changed as well - } - } - } - - if (successful) { - if (sideEffects.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { - world.getChunkProvider().getLightManager().checkBlock(pos); - } - markAndNotifyBlock(world, pos, chunk, old, newState, sideEffects); - } - - return successful; + return nativeAccess.setBlock(position, block, sideEffects); } @Override 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 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 + nativeAccess.applySideEffects(position, previousType, sideEffectSet); return Sets.intersection(ForgeWorldEdit.inst.getPlatform().getSupportedSideEffects(), sideEffectSet.getSideEffectsToApply()); } diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeWorldNativeAccess.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeWorldNativeAccess.java new file mode 100644 index 000000000..33784f1e7 --- /dev/null +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeWorldNativeAccess.java @@ -0,0 +1,152 @@ +/* + * 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.forge.internal; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.forge.ForgeAdapter; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.server.ChunkHolder; +import net.minecraft.world.server.ServerChunkProvider; + +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; +import java.util.Objects; + +public class ForgeWorldNativeAccess implements WorldNativeAccess { + private static final int UPDATE = 1, NOTIFY = 2; + + private final WeakReference world; + private SideEffectSet sideEffectSet; + + public ForgeWorldNativeAccess(WeakReference world) { + this.world = world; + } + + private World getWorld() { + return Objects.requireNonNull(world.get(), "The reference to the world was lost"); + } + + @Override + public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + + @Override + public Chunk getChunk(int x, int z) { + return getWorld().getChunk(x, z); + } + + @Override + public BlockState toNative(com.sk89q.worldedit.world.block.BlockState state) { + int stateId = BlockStateIdAccess.getBlockStateId(state); + return BlockStateIdAccess.isValidInternalId(stateId) + ? Block.getStateById(stateId) + : ForgeAdapter.adapt(state); + } + + @Override + public BlockState getBlockState(Chunk chunk, BlockPos position) { + return chunk.getBlockState(position); + } + + @Nullable + @Override + public BlockState setBlockState(Chunk chunk, BlockPos position, BlockState state) { + return chunk.setBlockState(position, state, false); + } + + @Override + public BlockState getValidBlockForPosition(BlockState block, BlockPos position) { + return Block.getValidBlockForPosition(block, getWorld(), position); + } + + @Override + public BlockPos getPosition(int x, int y, int z) { + return new BlockPos(x, y, z); + } + + @Override + public void updateLightingForBlock(BlockPos position) { + getWorld().getChunkProvider().getLightManager().checkBlock(position); + } + + @Override + public boolean updateTileEntity(BlockPos position, CompoundTag tag) { + CompoundNBT nativeTag = NBTConverter.toNative(tag); + return TileEntityUtils.setTileEntity(getWorld(), position, nativeTag); + } + + @Override + public void notifyBlockUpdate(BlockPos position, BlockState oldState, BlockState newState) { + getWorld().notifyBlockUpdate(position, oldState, newState, UPDATE | NOTIFY); + } + + @Override + public boolean isChunkTicking(Chunk chunk) { + return chunk.getLocationType().isAtLeast(ChunkHolder.LocationType.TICKING); + } + + @Override + public void markBlockChanged(BlockPos position) { + ((ServerChunkProvider) getWorld().getChunkProvider()).markBlockChanged(position); + } + + @Override + public void notifyNeighbors(BlockPos pos, BlockState oldState, BlockState newState) { + World world = getWorld(); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + world.notifyNeighbors(pos, oldState.getBlock()); + } else { + // Manually update each side + Block block = oldState.getBlock(); + world.neighborChanged(pos.west(), block, pos); + world.neighborChanged(pos.east(), block, pos); + world.neighborChanged(pos.down(), block, pos); + world.neighborChanged(pos.up(), block, pos); + world.neighborChanged(pos.north(), block, pos); + world.neighborChanged(pos.south(), block, pos); + } + if (newState.hasComparatorInputOverride()) { + world.updateComparatorOutputLevel(pos, newState.getBlock()); + } + } + + @Override + public void updateNeighbors(BlockPos pos, BlockState oldState, BlockState newState) { + World world = getWorld(); + oldState.updateDiagonalNeighbors(world, pos, NOTIFY); + newState.updateNeighbors(world, pos, NOTIFY); + newState.updateDiagonalNeighbors(world, pos, NOTIFY); + } + + @Override + public void onBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) { + getWorld().onBlockStateChange(pos, oldState, newState); + } +} diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/TileEntityUtils.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/TileEntityUtils.java index 29d2b187e..dc3297c7b 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/TileEntityUtils.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/TileEntityUtils.java @@ -24,13 +24,10 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.worldedit.math.BlockVector3; import net.minecraft.nbt.CompoundNBT; -import net.minecraft.nbt.IntNBT; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; -import javax.annotation.Nullable; - /** * Utility methods for setting tile entities in the world. */ @@ -39,39 +36,21 @@ public final class TileEntityUtils { private TileEntityUtils() { } - /** - * Update the given tag compound with position information. - * - * @param tag the tag - * @param position the position - */ - private static void updateForSet(CompoundNBT tag, BlockVector3 position) { - checkNotNull(tag); - checkNotNull(position); - - tag.put("x", new IntNBT(position.getBlockX())); - tag.put("y", new IntNBT(position.getBlockY())); - tag.put("z", new IntNBT(position.getBlockZ())); - } - /** * Set a tile entity at the given location using the tile entity ID from * the tag. * * @param world the world * @param position the position - * @param tag the tag for the tile entity (may be null to do nothing) + * @param tag the tag for the tile entity */ - static boolean setTileEntity(World world, BlockVector3 position, @Nullable CompoundNBT tag) { - if (tag != null) { - updateForSet(tag, position); - TileEntity tileEntity = TileEntity.create(tag); - if (tileEntity == null) { - return false; - } - world.setTileEntity(new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ()), tileEntity); - return true; + static boolean setTileEntity(World world, BlockPos position, CompoundNBT tag) { + TileEntity tileEntity = TileEntity.create(tag); + if (tileEntity == null) { + return false; } + world.setTileEntity(new BlockPos(position.getX(), position.getY(), position.getZ()), tileEntity); + return true; } public static CompoundNBT copyNbtData(TileEntity tile) { From f84958957cbf28793662c675f4ccc17769b5c10c Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Tue, 30 Jun 2020 13:44:26 +0100 Subject: [PATCH 04/11] Fix various lighting issues fixes #496 possibly fixes #497 --- .../adapter/mc1_14/BukkitGetBlocks_1_14.java | 43 ++++++++++++++++--- .../adapter/mc1_15/BukkitGetBlocks_1_15.java | 43 ++++++++++++++++--- .../mc1_15_2/BukkitGetBlocks_1_15_2.java | 37 +++++++++++++--- .../implementation/blocks/CharSetBlocks.java | 12 ++++++ 4 files changed, 115 insertions(+), 20 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java index eace17737..7196fb21b 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java @@ -56,7 +56,7 @@ import net.minecraft.server.v1_14_R1.Entity; import net.minecraft.server.v1_14_R1.EntityTypes; import net.minecraft.server.v1_14_R1.EnumSkyBlock; import net.minecraft.server.v1_14_R1.IBlockData; -import net.minecraft.server.v1_14_R1.LightEngineThreaded; +import net.minecraft.server.v1_14_R1.LightEngine; import net.minecraft.server.v1_14_R1.NBTTagCompound; import net.minecraft.server.v1_14_R1.NBTTagInt; import net.minecraft.server.v1_14_R1.NibbleArray; @@ -69,8 +69,13 @@ import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; import org.bukkit.craftbukkit.v1_14_R1.block.CraftBlock; import org.bukkit.event.entity.CreatureSpawnEvent; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class BukkitGetBlocks_1_14 extends CharGetBlocks { + + private static final Logger log = LoggerFactory.getLogger(BukkitGetBlocks_1_14.class); + public ChunkSection[] sections; public Chunk nmsChunk; public WorldServer world; @@ -129,7 +134,17 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { int layer = y >> 4; if (skyLight[layer] == null) { //getDataLayerData - skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPosition); + // If the server hasn't generated the section's NibbleArray yet, it will be null + if (nibbleArray == null) { + byte[] a = new byte[2048]; + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(a, (byte) 15); + nibbleArray = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.SKY, sectionPosition, nibbleArray); + } + skyLight[layer] = nibbleArray; } long l = BlockPosition.a(x, y, z); return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); @@ -140,7 +155,17 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { int layer = y >> 4; if (blockLight[layer] == null) { //getDataLayerData - blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition); + // If the server hasn't generated the section's NibbleArray yet, it will be null + if (nibbleArray == null) { + byte[] a = new byte[2048]; + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(a, (byte) 15); + nibbleArray = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.BLOCK, sectionPosition, nibbleArray); + } + blockLight[layer] = nibbleArray; } long l = BlockPosition.a(x, y, z); return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); @@ -299,7 +324,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { } else { existingSection = sections[layer]; if (existingSection == null) { - System.out.println("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); + log.error("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); continue; } } @@ -326,7 +351,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { } newSection = BukkitAdapter_1_14.newChunkSection(layer, this::load, setArr, fastmode); if (!BukkitAdapter_1_14.setSectionAtomic(sections, existingSection, newSection, layer)) { - System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); + log.error("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); continue; } else { updateGet(this, nmsChunk, sections, newSection, setArr, layer); @@ -667,9 +692,13 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { if (light[Y] == null) { continue; } - NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), Y); + NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(sectionPosition); if (nibble == null) { - continue; + byte[] a = new byte[2048]; + Arrays.fill(a, skyBlock == EnumSkyBlock.SKY ? (byte) 15 : (byte) 0); + nibble = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.SKY, sectionPosition, nibble); } synchronized (nibble) { for (int i = 0; i < 4096; i++) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java index c9fbca585..57cc44026 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java @@ -57,7 +57,7 @@ import net.minecraft.server.v1_15_R1.Entity; import net.minecraft.server.v1_15_R1.EntityTypes; import net.minecraft.server.v1_15_R1.EnumSkyBlock; import net.minecraft.server.v1_15_R1.IBlockData; -import net.minecraft.server.v1_15_R1.LightEngineThreaded; +import net.minecraft.server.v1_15_R1.LightEngine; import net.minecraft.server.v1_15_R1.NBTTagCompound; import net.minecraft.server.v1_15_R1.NBTTagInt; import net.minecraft.server.v1_15_R1.NibbleArray; @@ -70,8 +70,13 @@ import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; import org.bukkit.craftbukkit.v1_15_R1.block.CraftBlock; import org.bukkit.event.entity.CreatureSpawnEvent; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class BukkitGetBlocks_1_15 extends CharGetBlocks { + + private static final Logger log = LoggerFactory.getLogger(BukkitGetBlocks_1_15.class); + private static final Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); private final static Function nmsTile2We = tileEntity -> new LazyCompoundTag_1_15(Suppliers.memoize(() -> tileEntity.save(new NBTTagCompound()))); public ChunkSection[] sections; @@ -136,7 +141,17 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { public int getSkyLight(int x, int y, int z) { int layer = y >> 4; if (skyLight[layer] == null) { - skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPosition); + // If the server hasn't generated the section's NibbleArray yet, it will be null + if (nibbleArray == null) { + byte[] a = new byte[2048]; + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(a, (byte) 15); + nibbleArray = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.SKY, sectionPosition, nibbleArray); + } + skyLight[layer] = nibbleArray; } long l = BlockPosition.a(x, y, z); return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); @@ -146,7 +161,17 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { public int getEmmittedLight(int x, int y, int z) { int layer = y >> 4; if (blockLight[layer] == null) { - blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition); + // If the server hasn't generated the section's NibbleArray yet, it will be null + if (nibbleArray == null) { + byte[] a = new byte[2048]; + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(a, (byte) 15); + nibbleArray = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.BLOCK, sectionPosition, nibbleArray); + } + blockLight[layer] = nibbleArray; } long l = BlockPosition.a(x, y, z); return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); @@ -305,7 +330,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { } else { existingSection = sections[layer]; if (existingSection == null) { - System.out.println("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); + log.error("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); continue; } } @@ -332,7 +357,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { } newSection = BukkitAdapter_1_15.newChunkSection(layer, this::load, setArr, fastmode); if (!BukkitAdapter_1_15.setSectionAtomic(sections, existingSection, newSection, layer)) { - System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); + log.error("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); continue; } else { updateGet(this, nmsChunk, sections, newSection, setArr, layer); @@ -684,9 +709,13 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { if (light[Y] == null) { continue; } - NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), Y); + NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(sectionPosition); if (nibble == null) { - continue; + byte[] a = new byte[2048]; + Arrays.fill(a, skyBlock == EnumSkyBlock.SKY ? (byte) 15 : (byte) 0); + nibble = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.SKY, sectionPosition, nibble); } synchronized (nibble) { for (int i = 0; i < 4096; i++) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java index 3292cc556..90dc417b4 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java @@ -40,6 +40,7 @@ import net.minecraft.server.v1_15_R1.Entity; import net.minecraft.server.v1_15_R1.EntityTypes; import net.minecraft.server.v1_15_R1.EnumSkyBlock; import net.minecraft.server.v1_15_R1.IBlockData; +import net.minecraft.server.v1_15_R1.LightEngine; import net.minecraft.server.v1_15_R1.NBTTagCompound; import net.minecraft.server.v1_15_R1.NBTTagInt; import net.minecraft.server.v1_15_R1.NibbleArray; @@ -140,7 +141,17 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { public int getSkyLight(int x, int y, int z) { int layer = y >> 4; if (skyLight[layer] == null) { - skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPosition); + // If the server hasn't generated the section's NibbleArray yet, it will be null + if (nibbleArray == null) { + byte[] a = new byte[2048]; + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(a, (byte) 15); + nibbleArray = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.SKY, sectionPosition, nibbleArray); + } + skyLight[layer] = nibbleArray; } long l = BlockPosition.a(x, y, z); return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); @@ -150,7 +161,17 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { public int getEmmittedLight(int x, int y, int z) { int layer = y >> 4; if (blockLight[layer] == null) { - blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition); + // If the server hasn't generated the section's NibbleArray yet, it will be null + if (nibbleArray == null) { + byte[] a = new byte[2048]; + // Safe enough to assume if it's not created, it's not got any emitted light. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(a, (byte) 0); + nibbleArray = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.BLOCK, sectionPosition, nibbleArray); + } + blockLight[layer] = nibbleArray; } long l = BlockPosition.a(x, y, z); return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); @@ -316,7 +337,7 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { } else { existingSection = sections[layer]; if (existingSection == null) { - System.out.println("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); + log.error("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); continue; } } @@ -343,7 +364,7 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { } newSection = BukkitAdapter_1_15_2.newChunkSection(layer, this::load, setArr, fastmode); if (!BukkitAdapter_1_15_2.setSectionAtomic(sections, existingSection, newSection, layer)) { - System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); + log.error("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); continue; } else { updateGet(this, nmsChunk, sections, newSection, setArr, layer); @@ -689,9 +710,13 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { if (light[Y] == null) { continue; } - NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), Y); + NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(sectionPosition); if (nibble == null) { - continue; + byte[] a = new byte[2048]; + Arrays.fill(a, skyBlock == EnumSkyBlock.SKY ? (byte) 15 : (byte) 0); + nibble = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.SKY, sectionPosition, nibble); } synchronized (nibble) { for (int i = 0; i < 4096; i++) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java index 1dda73c3a..744e92f06 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java @@ -181,6 +181,18 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { } @Override public void setFullBright(int layer) { + if (light == null) { + light = new char[16][]; + } + if (light[layer] == null) { + light[layer] = new char[4096]; + } + if (skyLight == null) { + skyLight = new char[16][]; + } + if (skyLight[layer] == null) { + skyLight[layer] = new char[4096]; + } Arrays.fill(light[layer], (char) 15); Arrays.fill(skyLight[layer], (char) 15); } From 4ca19acb489b219518e964c2ff7ce3f16ad137bb Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Tue, 30 Jun 2020 13:51:27 +0100 Subject: [PATCH 05/11] implement 1.16 methods --- .../fawe/bukkit/wrapper/AsyncWorld.java | 32 ++++++++++++++----- .../wrapper/state/AsyncDataContainer.java | 11 +++++++ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java index 4252d3f32..c6b72076b 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java @@ -884,6 +884,14 @@ public class AsyncWorld extends PassthroughExtent implements World { parent.setWaterAnimalSpawnLimit(limit); } + @Override public int getWaterAmbientSpawnLimit() { + return 0; + } + + @Override public void setWaterAmbientSpawnLimit(int limit) { + + } + @Override public int getAmbientSpawnLimit() { return parent.getAmbientSpawnLimit(); @@ -1284,19 +1292,27 @@ public class AsyncWorld extends PassthroughExtent implements World { return parent.getHighestBlockAt(location, heightmap); } - public long getTicksPerWaterSpawns() { - throw new UnsupportedOperationException(); + public long getTicksPerWaterSpawns() throws UnsupportedOperationException { + return parent.getTicksPerWaterSpawns(); } - public void setTicksPerWaterSpawns(int ticksPerWaterSpawns) { - throw new UnsupportedOperationException(); + public void setTicksPerWaterSpawns(int ticksPerWaterSpawns) throws UnsupportedOperationException { + parent.setTicksPerWaterSpawns(ticksPerWaterSpawns); } - public long getTicksPerAmbientSpawns() { - throw new UnsupportedOperationException(); + @Override public long getTicksPerWaterAmbientSpawns() { + return parent.getTicksPerWaterAmbientSpawns(); } - public void setTicksPerAmbientSpawns(int ticksPerAmbientSpawns) { - throw new UnsupportedOperationException(); + @Override public void setTicksPerWaterAmbientSpawns(int ticksPerAmbientSpawns) { + parent.setTicksPerWaterAmbientSpawns(ticksPerAmbientSpawns); + } + + public long getTicksPerAmbientSpawns() throws UnsupportedOperationException { + return parent.getTicksPerAmbientSpawns(); + } + + public void setTicksPerAmbientSpawns(int ticksPerAmbientSpawns) throws UnsupportedOperationException { + parent.setTicksPerAmbientSpawns(ticksPerAmbientSpawns); } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java index f1fbbcbb2..51da9f50c 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java @@ -5,16 +5,21 @@ import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Objects; +import java.util.Set; + import org.apache.commons.lang.Validate; import org.bukkit.NamespacedKey; import org.bukkit.persistence.PersistentDataAdapterContext; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.NotNull; public final class AsyncDataContainer implements PersistentDataContainer { private final CompoundTag root; + private final Set keys = new HashSet<>(); public AsyncDataContainer(CompoundTag root) { this.root = root; @@ -47,6 +52,7 @@ public final class AsyncDataContainer implements PersistentDataContainer { Validate.notNull(type, "The provided type for the custom value was null"); Validate.notNull(value, "The provided value for the custom value was null"); get().put(key.toString(), FaweCache.IMP.asTag(type.toPrimitive(value, null))); + keys.add(key); } public boolean has(NamespacedKey key, PersistentDataType type) { @@ -69,9 +75,14 @@ public final class AsyncDataContainer implements PersistentDataContainer { return z != null ? z : defaultValue; } + @Override public @NotNull Set getKeys() { + return keys; + } + public void remove(NamespacedKey key) { Validate.notNull(key, "The provided key for the custom value was null"); get(false).remove(key.toString()); + keys.remove(key); } public boolean isEmpty() { From 3f3c49c0a81afdb90e58af6a1a575356eb824dc3 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Wed, 1 Jul 2020 12:41:20 +0100 Subject: [PATCH 06/11] Looks like working block setting and removal --- .../com/boydti/fawe/bukkit/FaweBukkit.java | 8 ++ .../mc1_16_1/BukkitAdapter_1_16_1.java | 8 +- .../mc1_16_1/BukkitGetBlocks_1_16_1.java | 4 +- .../main/java/com/boydti/fawe/FaweCache.java | 98 ++++++++++++++- .../src/main/java/com/boydti/fawe/IFawe.java | 4 + .../java/com/boydti/fawe/beta/IBlocks.java | 13 +- .../implementation/packet/ChunkPacket.java | 5 +- .../collection/BitArrayUnstretched.java | 115 ++++++++++++++++++ 8 files changed, 244 insertions(+), 11 deletions(-) create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/object/collection/BitArrayUnstretched.java diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index 823904be1..b6a8a7755 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -58,6 +58,7 @@ public class FaweBukkit implements IFawe, Listener { private boolean listeningImages; private BukkitImageListener imageListener; private CFIPacketListener packetListener; + private final boolean chunksStretched; public VaultUtil getVault() { return this.vault; @@ -99,6 +100,8 @@ public class FaweBukkit implements IFawe, Listener { // The tick limiter new ChunkListener_9(); }); + + chunksStretched = Integer.parseInt(Bukkit.getMinecraftVersion().split("\\.")[1]) >= 16; } @Override // Please don't delete this again, it's WIP @@ -302,6 +305,11 @@ public class FaweBukkit implements IFawe, Listener { return null; } + @Override + public boolean isChunksStretched() { + return chunksStretched; + } + private void setupPlotSquared() { Plugin plotSquared = this.plugin.getServer().getPluginManager().getPlugin("PlotSquared"); if (plotSquared == null) diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java index 7b1549d65..a8e096e0c 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java @@ -5,7 +5,7 @@ import com.boydti.fawe.FaweCache; import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.bukkit.adapter.NMSAdapter; import com.boydti.fawe.config.Settings; -import com.boydti.fawe.object.collection.BitArray; +import com.boydti.fawe.object.collection.BitArrayUnstretched; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.TaskManager; @@ -232,11 +232,13 @@ public final class BukkitAdapter_1_16_1 extends NMSAdapter { bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries } - final int blockBitArrayEnd = (bitsPerEntry * 4096) >> 6; + final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntry); + final int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong); + if (num_palette == 1) { for (int i = 0; i < blockBitArrayEnd; i++) blockStates[i] = 0; } else { - final BitArray bitArray = new BitArray(bitsPerEntry, 4096, blockStates); + final BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntry, blockStates); bitArray.fromRaw(blocksCopy); } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java index b07589339..a4eed61aa 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java @@ -10,7 +10,7 @@ import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.bukkit.adapter.mc1_16_1.nbt.LazyCompoundTag_1_16_1; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.AdaptedMap; -import com.boydti.fawe.object.collection.BitArray; +import com.boydti.fawe.object.collection.BitArrayUnstretched; import com.google.common.base.Suppliers; import com.google.common.collect.Iterables; import com.sk89q.jnbt.Tag; @@ -553,7 +553,7 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { final int bitsPerEntry = (int) BukkitAdapter_1_16_1.fieldBitsPerEntry.get(bits); final long[] blockStates = bits.a(); - new BitArray(bitsPerEntry, 4096, blockStates).toRaw(data); + new BitArrayUnstretched(bitsPerEntry, blockStates).toRaw(data); int num_palette; if (palette instanceof DataPaletteLinear) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java b/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java index ee43fdc36..859503291 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java @@ -1,7 +1,6 @@ package com.boydti.fawe; import static com.google.common.base.Preconditions.checkNotNull; -import static org.slf4j.LoggerFactory.getLogger; import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.Trimable; @@ -9,6 +8,7 @@ import com.boydti.fawe.beta.implementation.queue.Pool; import com.boydti.fawe.beta.implementation.queue.QueuePool; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.BitArray; +import com.boydti.fawe.object.collection.BitArrayUnstretched; import com.boydti.fawe.object.collection.CleanableThreadLocal; import com.boydti.fawe.object.collection.VariableThreadLocal; import com.boydti.fawe.object.exception.FaweBlockBagException; @@ -299,6 +299,102 @@ public enum FaweCache implements Trimable { } } + /** + * Convert raw int array to unstretched palette (1.16) + * @param layerOffset + * @param blocks + * @return palette + */ + public Palette toPaletteUnstretched(int layerOffset, char[] blocks) { + return toPaletteUnstretched(layerOffset, null, blocks); + } + + /** + * Convert raw int array to unstretched palette (1.16) + * @param layerOffset + * @param blocks + * @return palette + */ + public Palette toPaletteUnstretched(int layerOffset, int[] blocks) { + return toPaletteUnstretched(layerOffset, blocks, null); + } + + private Palette toPaletteUnstretched(int layerOffset, int[] blocksInts, char[] blocksChars) { + int[] blockToPalette = BLOCK_TO_PALETTE.get(); + int[] paletteToBlock = PALETTE_TO_BLOCK.get(); + long[] blockStates = BLOCK_STATES.get(); + int[] blocksCopy = SECTION_BLOCKS.get(); + + try { + int num_palette = 0; + int blockIndexStart = layerOffset << 12; + int blockIndexEnd = blockIndexStart + 4096; + if (blocksChars != null) { + for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) { + int ordinal = blocksChars[i]; + int palette = blockToPalette[ordinal]; + if (palette == Integer.MAX_VALUE) { + blockToPalette[ordinal] = palette = num_palette; + paletteToBlock[num_palette] = ordinal; + num_palette++; + } + blocksCopy[j] = palette; + } + } else if (blocksInts != null) { + for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) { + int ordinal = blocksInts[i]; + int palette = blockToPalette[ordinal]; + if (palette == Integer.MAX_VALUE) { + // BlockState state = BlockTypesCache.states[ordinal]; + blockToPalette[ordinal] = palette = num_palette; + paletteToBlock[num_palette] = ordinal; + num_palette++; + } + blocksCopy[j] = palette; + } + } else { + throw new IllegalArgumentException(); + } + + for (int i = 0; i < num_palette; i++) { + blockToPalette[paletteToBlock[i]] = Integer.MAX_VALUE; + } + + // BlockStates + int bitsPerEntry = MathMan.log2nlz(num_palette - 1); + if (Settings.IMP.PROTOCOL_SUPPORT_FIX || num_palette != 1) { + bitsPerEntry = Math.max(bitsPerEntry, 4); // Protocol support breaks <4 bits per entry + } else { + bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries + } + int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntry); + int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong); + if (num_palette == 1) { + // Set a value, because minecraft needs it for some reason + blockStates[0] = 0; + blockBitArrayEnd = 1; + } else { + BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntry, blockStates); + bitArray.fromRaw(blocksCopy); + } + + // Construct palette + Palette palette = PALETTE_CACHE.get(); + palette.bitsPerEntry = bitsPerEntry; + palette.paletteToBlockLength = num_palette; + palette.paletteToBlock = paletteToBlock; + + palette.blockStatesLength = blockBitArrayEnd; + palette.blockStates = blockStates; + + return palette; + } catch (Throwable e) { + e.printStackTrace(); + Arrays.fill(blockToPalette, Integer.MAX_VALUE); + throw e; + } + } + /* * Vector cache */ diff --git a/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java b/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java index 20a15faab..6941fb728 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java @@ -41,4 +41,8 @@ public interface IFawe { Preloader getPreloader(); + default boolean isChunksStretched() { + return true; + } + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java index c042e7a23..42fbac580 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java @@ -45,11 +45,11 @@ public interface IBlocks extends Trimable { IBlocks reset(); - default byte[] toByteArray(boolean full) { - return toByteArray(null, getBitMask(), full); + default byte[] toByteArray(boolean full, boolean stretched) { + return toByteArray(null, getBitMask(), full, stretched); } - default byte[] toByteArray(byte[] buffer, int bitMask, boolean full) { + default byte[] toByteArray(byte[] buffer, int bitMask, boolean full, boolean stretched) { if (buffer == null) { buffer = new byte[1024]; } @@ -81,7 +81,12 @@ public interface IBlocks extends Trimable { } sectionWriter.writeShort(nonEmpty); // non empty - FaweCache.Palette palette = FaweCache.IMP.toPalette(0, ids); + FaweCache.Palette palette; + if (stretched) { + palette = FaweCache.IMP.toPalette(0, ids); + } else { + palette = FaweCache.IMP.toPaletteUnstretched(0, ids); + } sectionWriter.writeByte(palette.bitsPerEntry); // bits per block sectionWriter.writeVarInt(palette.paletteToBlockLength); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/packet/ChunkPacket.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/packet/ChunkPacket.java index b904c5cd6..5638c962e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/packet/ChunkPacket.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/packet/ChunkPacket.java @@ -1,10 +1,12 @@ package com.boydti.fawe.beta.implementation.packet; +import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IBlocks; import com.boydti.fawe.object.FaweOutputStream; import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEdit; import java.util.HashMap; import java.util.function.Function; @@ -64,7 +66,7 @@ public class ChunkPacket implements Function, Supplier { if (sectionBytes == null) { IBlocks tmpChunk = getChunk(); byte[] buf = FaweCache.IMP.BYTE_BUFFER_8192.get(); - sectionBytes = tmpChunk.toByteArray(buf, tmpChunk.getBitMask(), this.full); + sectionBytes = tmpChunk.toByteArray(buf, tmpChunk.getBitMask(), this.full, Fawe.imp().isChunksStretched()); } tmp = sectionBytes; } @@ -72,6 +74,7 @@ public class ChunkPacket implements Function, Supplier { return tmp; } + public Object getNativePacket() { return nativePacket; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BitArrayUnstretched.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BitArrayUnstretched.java new file mode 100644 index 000000000..9572eecc9 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BitArrayUnstretched.java @@ -0,0 +1,115 @@ +package com.boydti.fawe.object.collection; + +import com.boydti.fawe.util.MathMan; + +public final class BitArrayUnstretched { + + private final long[] data; + private final int bitsPerEntry; + private final int maxSeqLocIndex; + private final int emptyBitCount; + private final long mask; + private final int longLen; + + public BitArrayUnstretched(int bitsPerEntry, long[] buffer) { + this.bitsPerEntry = bitsPerEntry; + this.mask = (1L << bitsPerEntry) - 1L; + this.emptyBitCount = 64 % bitsPerEntry; + this.maxSeqLocIndex = 64 - (bitsPerEntry + emptyBitCount); + final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntry); + this.longLen = MathMan.ceilZero((float) 4096 / blocksPerLong); + if (buffer.length < longLen) { + this.data = new long[longLen]; + } else { + this.data = buffer; + } + } + + public long[] getData() { + return data; + } + + public final void set(int index, int value) { + if (longLen == 0) return; + int bitIndexStart = index * bitsPerEntry + MathMan.floorZero((double) index / longLen) * emptyBitCount; + int longIndexStart = bitIndexStart >> 6; + int localBitIndexStart = bitIndexStart & 63; + this.data[longIndexStart] = this.data[longIndexStart] & ~(mask << localBitIndexStart) | (long) value << localBitIndexStart; + } + + public final int get(int index) { + if (longLen == 0) return 0; + int bitIndexStart = index * bitsPerEntry + MathMan.floorZero((double) index / longLen) * emptyBitCount; + + int longIndexStart = bitIndexStart >> 6; + + int localBitIndexStart = bitIndexStart & 63; + return (int)(this.data[longIndexStart] >>> localBitIndexStart & mask); + } + + public int getLength() { + return longLen; + } + + public final void fromRaw(int[] arr) { + final long[] data = this.data; + final int bitsPerEntry = this.bitsPerEntry; + final int maxSeqLocIndex = this.maxSeqLocIndex; + + int localStart = 0; + int arrI = 0; + long l = 0; + for (int i = 0; i < longLen; i++) { + int lastVal; + for (; localStart <= maxSeqLocIndex && arrI < 4096; localStart += bitsPerEntry) { + lastVal = arr[arrI++]; + l |= ((long) lastVal << localStart); + } + localStart = 0; + data[i] = l; + l = 0; + } + } + + public final int[] toRaw() { + return toRaw(new int[4096]); + } + + public final int[] toRaw(int[] buffer) { + final long[] data = this.data; + final int bitsPerEntry = this.bitsPerEntry; + final int maxSeqLocIndex = this.maxSeqLocIndex; + + int localStart = 0; + int arrI = 0; + for (int i = 0; i < longLen; i++) { + long l = data[i]; + char lastVal; + for (; localStart <= maxSeqLocIndex && arrI < 4096; localStart += bitsPerEntry) { + lastVal = (char) (l >>> localStart & this.mask); + buffer[arrI++] = lastVal; + } + localStart = 0; + } + return buffer; + } + + public final char[] toRaw(char[] buffer) { + final long[] data = this.data; + final int bitsPerEntry = this.bitsPerEntry; + final int maxSeqLocIndex = this.maxSeqLocIndex; + + int localStart = 0; + int arrI = 0; + for (int i = 0; i < longLen; i++) { + long l = data[i]; + char lastVal; + for (; localStart <= maxSeqLocIndex && arrI < 4096; localStart += bitsPerEntry) { + lastVal = (char) (l >>> localStart & this.mask); + buffer[arrI++] = lastVal; + } + localStart = 0; + } + return buffer; + } +} From 6be429cc96ae6c7ad6574c822a50d9c3ed461a99 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Wed, 1 Jul 2020 12:55:58 +0100 Subject: [PATCH 07/11] Lighting: - bring fixes to 1.16 - Fix for all lighting where it only set sky lighting if not present, even if it was trying to set block lighting (unlikely to have caused issues as block lighting seems always to be present, but wrong nonetheless) --- .../adapter/mc1_14/BukkitGetBlocks_1_14.java | 2 +- .../mc1_15_2/BukkitGetBlocks_1_15_2.java | 2 +- .../mc1_16_1/BukkitGetBlocks_1_16_1.java | 41 +++++++++++++++---- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java index 7196fb21b..ae871b419 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java @@ -698,7 +698,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { byte[] a = new byte[2048]; Arrays.fill(a, skyBlock == EnumSkyBlock.SKY ? (byte) 15 : (byte) 0); nibble = new NibbleArray(a); - ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.SKY, sectionPosition, nibble); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(skyBlock, sectionPosition, nibble); } synchronized (nibble) { for (int i = 0; i < 4096; i++) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java index 90dc417b4..e0cbf7266 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java @@ -716,7 +716,7 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { byte[] a = new byte[2048]; Arrays.fill(a, skyBlock == EnumSkyBlock.SKY ? (byte) 15 : (byte) 0); nibble = new NibbleArray(a); - ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.SKY, sectionPosition, nibble); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(skyBlock, sectionPosition, nibble); } synchronized (nibble) { for (int i = 0; i < 4096; i++) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java index a4eed61aa..d6dada109 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java @@ -43,8 +43,7 @@ import static org.slf4j.LoggerFactory.getLogger; public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { - private static final Logger log = LoggerFactory.getLogger( - BukkitGetBlocks_1_16_1.class); + private static final Logger log = LoggerFactory.getLogger(BukkitGetBlocks_1_16_1.class); private static final Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); private final static Function nmsTile2We = tileEntity -> new LazyCompoundTag_1_16_1(Suppliers.memoize(() -> tileEntity.save(new NBTTagCompound()))); @@ -110,7 +109,17 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { public int getSkyLight(int x, int y, int z) { int layer = y >> 4; if (skyLight[layer] == null) { - skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPosition); + // If the server hasn't generated the section's NibbleArray yet, it will be null + if (nibbleArray == null) { + byte[] a = new byte[2048]; + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(a, (byte) 15); + nibbleArray = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.SKY, sectionPosition, nibbleArray, true); + } + skyLight[layer] = nibbleArray; } long l = BlockPosition.a(x, y, z); return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); @@ -119,8 +128,18 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { @Override public int getEmmittedLight(int x, int y, int z) { int layer = y >> 4; - if (blockLight[layer] == null) { - blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer)); + if (skyLight[layer] == null) { + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition); + // If the server hasn't generated the section's NibbleArray yet, it will be null + if (nibbleArray == null) { + byte[] a = new byte[2048]; + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(a, (byte) 15); + nibbleArray = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.BLOCK, sectionPosition, nibbleArray, true); + } + skyLight[layer] = nibbleArray; } long l = BlockPosition.a(x, y, z); return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); @@ -286,7 +305,7 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { } else { existingSection = sections[layer]; if (existingSection == null) { - System.out.println("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); + log.error("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); continue; } } @@ -315,7 +334,7 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { .newChunkSection(layer, this::load, setArr, fastmode); if (!BukkitAdapter_1_16_1 .setSectionAtomic(sections, existingSection, newSection, layer)) { - System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); + log.error("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); continue; } else { updateGet(this, nmsChunk, sections, newSection, setArr, layer); @@ -662,9 +681,13 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { if (light[Y] == null) { continue; } - NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), Y); + NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(sectionPosition); if (nibble == null) { - continue; + byte[] a = new byte[2048]; + Arrays.fill(a, skyBlock == EnumSkyBlock.SKY ? (byte) 15 : (byte) 0); + nibble = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(skyBlock, sectionPosition, nibble, true); } synchronized (nibble) { for (int i = 0; i < 4096; i++) { From 0bb6bc3563526a56d26f7640bb1b22a16ccf70ce Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Wed, 1 Jul 2020 14:01:39 +0200 Subject: [PATCH 08/11] Start reimplementation of simplex pattern (#520) * Start reimplementation of simplex pattern * Fix suggestions * Allow nested weighted patterns * Add documentation and improve error handling * Remove unnecessary code and obsolete TODOs --- .../main/java/com/sk89q/util/StringUtil.java | 34 +++++ .../extension/factory/BlockFactory.java | 4 +- .../extension/factory/PatternFactory.java | 4 + .../extension/factory/parser/RichParser.java | 118 ++++++++++++++++++ .../parser/pattern/RandomPatternParser.java | 12 +- .../parser/pattern/SimplexPatternParser.java | 75 +++++++++++ .../function/pattern/RandomPattern.java | 13 ++ 7 files changed, 253 insertions(+), 7 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/RichParser.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/SimplexPatternParser.java diff --git a/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java b/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java index 1aceda930..8231d3aa8 100644 --- a/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java +++ b/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java @@ -20,6 +20,7 @@ package com.sk89q.util; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Locale; @@ -331,4 +332,37 @@ public final class StringUtil { return parsableBlocks; } + + /** + * Splits a string respecting enclosing quotes. + * + * @param input the input to split. + * @param delimiter the delimiter to split on. + * @param open the opening quote character. + * @param close the closing quote character. + * @return a list of split strings. + */ + public static List split(String input, char delimiter, char open, char close) { + if (input.indexOf(open) == -1 && input.indexOf(close) == -1) { + return Arrays.asList(input.split(String.valueOf(delimiter))); + } + int level = 0; + int begin = 0; + List split = new ArrayList<>(); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c == delimiter && level == 0) { + split.add(input.substring(begin, i)); + begin = i + 1; + } else if (c == open) { + level++; + } else if (c == close) { + level--; + } + } + if (begin < input.length()) { + split.add(input.substring(begin)); + } + return split; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java index 4f5a4fdbc..693cd9b8e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java @@ -58,8 +58,8 @@ public class BlockFactory extends AbstractFactory { */ public Set parseFromListInput(String input, ParserContext context) throws InputParseException { Set blocks = new HashSet<>(); - String[] splits = input.split(","); - for (String token : StringUtil.parseListInQuotes(splits, ',', '[', ']', true)) { + // String[] splits = input.split(","); + for (String token : StringUtil.split(input, ',', '[', ']')) { blocks.add(parseFromInput(token, context)); } return blocks; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java index 014229a3e..41bece423 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java @@ -24,6 +24,7 @@ import com.sk89q.worldedit.extension.factory.parser.pattern.BlockCategoryPattern import com.sk89q.worldedit.extension.factory.parser.pattern.ClipboardPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.RandomPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.RandomStatePatternParser; +import com.sk89q.worldedit.extension.factory.parser.pattern.SimplexPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.SingleBlockPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.TypeOrStateApplyingPatternParser; import com.sk89q.worldedit.function.pattern.Pattern; @@ -54,6 +55,9 @@ public final class PatternFactory extends AbstractFactory { register(new TypeOrStateApplyingPatternParser(worldEdit)); register(new RandomStatePatternParser(worldEdit)); register(new BlockCategoryPatternParser(worldEdit)); + + // FAWE + register(new SimplexPatternParser(worldEdit)); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/RichParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/RichParser.java new file mode 100644 index 000000000..6bd9261fa --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/RichParser.java @@ -0,0 +1,118 @@ +package com.sk89q.worldedit.extension.factory.parser; + +import com.sk89q.util.StringUtil; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.internal.registry.InputParser; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringJoiner; +import java.util.stream.Stream; + +/** + * A rich parser allows parsing of patterns and masks with extra arguments, + * e.g. #simplex[scale][pattern]. + * + * @param the parse result. + */ +public abstract class RichParser extends InputParser { + private final String prefix; + private final String required; + + /** + * Create a new rich parser with a defined prefix for the result, e.g. {@code #simplex}. + * + * @param worldEdit the worldedit instance. + * @param prefix the prefix of this parser result. + */ + protected RichParser(WorldEdit worldEdit, String prefix) { + super(worldEdit); + this.prefix = prefix; + this.required = prefix + "["; + } + + @Override + public Stream getSuggestions(String input) { + // we don't even want to start suggesting if it's not meant to be this parser result + if (input.length() > this.required.length() && !input.startsWith(this.required)) { + return Stream.empty(); + } + // suggest until the first [ as long as it isn't fully typed + if (input.length() < this.required.length()) { + return Stream.of(this.required).filter(s -> s.startsWith(input)); + } + // we know that it is at least "" + String[] strings = extractArguments(input.substring(this.prefix.length()), false); + StringJoiner joiner = new StringJoiner(","); + for (int i = 0; i < strings.length - 1; i++) { + joiner.add("[" + strings[i] + "]"); + } + String previous = this.prefix + joiner; + return getSuggestions(strings[strings.length - 1], strings.length - 1).map(s -> previous + "[" + s + "]"); + } + + @Override + public E parseFromInput(String input, ParserContext context) throws InputParseException { + if (!input.startsWith(this.prefix)) return null; + if (input.length() < this.prefix.length()) return null; + String[] arguments = extractArguments(input.substring(prefix.length()), true); + return parseFromInput(arguments, context); + } + + /** + * Returns a stream of suggestions for the argument at the given index. + * + * @param argumentInput the already provided input for the argument at the given index. + * @param index the index of the argument to get suggestions for. + * @return a stream of suggestions matching the given input for the argument at the given index. + */ + protected abstract Stream getSuggestions(String argumentInput, int index); + + /** + * Parses the already split arguments. + * + * @param arguments the array of arguments that were split (can be empty). + * @param context the context of this parsing process. + * @return the resulting parsed type. + * @throws InputParseException if the input couldn't be parsed correctly. + */ + protected abstract E parseFromInput(@NotNull String[] arguments, ParserContext context) throws InputParseException; + + /** + * Extracts arguments enclosed by {@code []} into an array. + * Example: {@code [Hello][World]} results in a list containing {@code Hello} and {@code World}. + * + * @param input the input to extract arguments from. + * @param requireClosing whether or not the extraction requires valid bracketing. + * @return an array of extracted arguments. + * @throws InputParseException if {@code requireClosing == true} and the count of [ != the count of ] + */ + protected String[] extractArguments(String input, boolean requireClosing) throws InputParseException { + int open = 0; // the "level" + int openIndex = 0; + int i = 0; + List arguments = new ArrayList<>(); + for (; i < input.length(); i++) { + if (input.charAt(i) == '[') { + if (open++ == 0) { + openIndex = i; + } + } + if (input.charAt(i) == ']') { + if (--open == 0) { + arguments.add(input.substring(openIndex + 1, i)); + } + } + } + if (!requireClosing && open > 0) { + arguments.add(input.substring(openIndex + 1)); + } + if (requireClosing && open != 0) { + throw new InputParseException("Invalid bracketing, are you missing a '[' or ']'?"); + } + return arguments.toArray(new String[0]); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java index 81b1b11e3..66c55be3e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java @@ -38,8 +38,9 @@ public class RandomPatternParser extends InputParser { @Override public Stream getSuggestions(String input) { - String[] splits = input.split(",", -1); - List patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true); + List patterns = StringUtil.split(input, ',', '[', ']'); + /*String[] splits = input.split(",", -1); + List patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true);*/ if (patterns.size() == 1) { return Stream.empty(); } @@ -63,8 +64,9 @@ public class RandomPatternParser extends InputParser { public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { RandomPattern randomPattern = new RandomPattern(); - String[] splits = input.split(",", -1); - List patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true); + List patterns = StringUtil.split(input, ',', '[', ']'); + /*String[] splits = input.split(",", -1); + List patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true);*/ if (patterns.size() == 1) { return null; // let a 'single'-pattern parser handle it } @@ -74,7 +76,7 @@ public class RandomPatternParser extends InputParser { // Parse special percentage syntax if (token.matches("[0-9]+(\\.[0-9]*)?%.*")) { - String[] p = token.split("%"); + String[] p = token.split("%", 2); if (p.length < 2) { throw new InputParseException("Missing the type after the % symbol for '" + input + "'"); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/SimplexPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/SimplexPatternParser.java new file mode 100644 index 000000000..547f96390 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/SimplexPatternParser.java @@ -0,0 +1,75 @@ +package com.sk89q.worldedit.extension.factory.parser.pattern; + +import com.boydti.fawe.object.random.SimplexRandom; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.factory.parser.RichParser; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.pattern.RandomPattern; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import org.jetbrains.annotations.NotNull; + +import java.util.stream.Stream; + +public class SimplexPatternParser extends RichParser { + private static final String SIMPLEX_PREFIX = "#simplex"; + + public SimplexPatternParser(WorldEdit worldEdit) { + super(worldEdit, SIMPLEX_PREFIX); + } + + @Override + protected Stream getSuggestions(String argumentInput, int index) { + if (index == 0) { + if (argumentInput.isEmpty()) { + return Stream.of("1", "2", "3", "4", "5", "6", "7", "8", "9"); + } + // if already a valid number, suggest more digits + if (isDouble(argumentInput)) { + Stream numbers = Stream.of("", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"); + if (argumentInput.indexOf('.') == -1) { + numbers = Stream.concat(numbers, Stream.of(".")); + } + return numbers.map(s -> argumentInput + s); + } + // no valid input anymore + return Stream.empty(); + } + if (index == 1) { + return worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); + } + return Stream.empty(); + } + + @Override + protected Pattern parseFromInput(@NotNull String[] arguments, ParserContext context) { + if (arguments.length != 2) { + throw new InputParseException("Simplex requires a scale and a pattern, e.g. #simplex[5][dirt,stone]"); + } + double scale = Double.parseDouble(arguments[0]); + scale = 1d / Math.max(1, scale); + Pattern inner = worldEdit.getPatternFactory().parseFromInput(arguments[1], context); + if (inner instanceof RandomPattern) { + return new RandomPattern(new SimplexRandom(scale), (RandomPattern) inner); + } else if (inner instanceof BlockStateHolder) { + return inner; // single blocks won't have any impact on how simplex behaves + } else { + throw new InputParseException("Pattern " + inner.getClass().getSimpleName() + " cannot be used with #simplex"); + } + } + + private static boolean isDouble(String input) { + boolean point = false; + for (char c : input.toCharArray()) { + if (!Character.isDigit(c)) { + if (c == '.' && !point) { + point = true; + } else { + return false; + } + } + } + return true; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java index d5ecc2b44..3e02cdfaa 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java @@ -52,6 +52,19 @@ public class RandomPattern extends AbstractPattern { this.random = random; } + /** + * Create a random pattern from an existing one but with a different random. + * + * @param random the new random to use. + * @param parent the existing random pattern. + */ + public RandomPattern(SimpleRandom random, RandomPattern parent) { + this.random = random; + this.weights = parent.weights; + this.collection = RandomCollection.of(weights, random); + this.patterns = parent.patterns; + } + /** * Add a pattern to the weight list of patterns. * From efc1a888d852f377a01a4ebcaf2682f6ca70e33d Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Wed, 1 Jul 2020 13:17:19 +0100 Subject: [PATCH 09/11] update links, versions, etc --- README.md | 4 ++-- build.gradle.kts | 2 +- worldedit-core/src/main/java/com/boydti/fawe/Fawe.java | 2 +- worldedit-core/src/main/java/com/boydti/fawe/FaweVersion.java | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ae6f79e90..09b28f24b 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ FAWE is a fork of WorldEdit that has huge speed and memory improvements and cons ## Downloads ### 1.13+ * [Download](https://intellectualsites.github.io/download/fawe.html) -* [Jenkins](https://ci.athion.net/job/FastAsyncWorldEdit-1.15/) +* [Jenkins](https://ci.athion.net/job/FastAsyncWorldEdit-1.16/) ### < 1.12.2 * [Download](https://intellectualsites.github.io/download/fawe.html) @@ -35,7 +35,7 @@ $ gradlew setupDecompWorkspace $ gradlew build ``` -The jar is located in `worldedit-bukkit/build/libs/FastAsyncWorldEdit-1.15-###.jar` +The jar is located in `worldedit-bukkit/build/libs/FastAsyncWorldEdit-1.16-###.jar` ## Contributing Have an idea for an optimization, or a cool feature? diff --git a/build.gradle.kts b/build.gradle.kts index b6e47f76c..84e6e066f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ logger.lifecycle(""" ******************************************* """) //TODO FIX THIS WHEN I FEEL LIKE IT -var rootVersion = "1.15" +var rootVersion = "1.16" var revision: String = "" var buildNumber = "" var date: String = "" diff --git a/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java b/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java index 6f61bdda8..6675390eb 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java @@ -305,7 +305,7 @@ public class Fawe { br.close(); this.version = FaweVersion.tryParse(versionString, commitString, dateString); Settings.IMP.DATE = new Date(100 + version.year, version.month, version.day).toGMTString(); - Settings.IMP.BUILD = "https://ci.athion.net/job/FastAsyncWorldEdit-1.15/" + version.build; + Settings.IMP.BUILD = "https://ci.athion.net/job/FastAsyncWorldEdit-1.16/" + version.build; Settings.IMP.COMMIT = "https://github.com/IntellectualSites/FastAsyncWorldEdit/commit/" + Integer.toHexString(version.hash); } catch (Throwable ignore) {} try { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/FaweVersion.java b/worldedit-core/src/main/java/com/boydti/fawe/FaweVersion.java index 7fd480dfb..3c4f08062 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/FaweVersion.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/FaweVersion.java @@ -32,9 +32,9 @@ public class FaweVersion { @Override public String toString() { if (hash == 0 && build == 0) { - return "FastAsyncWorldEdit-1.15-NoVer-SNAPSHOT"; + return "FastAsyncWorldEdit-1.16-NoVer-SNAPSHOT"; } else { - return "FastAsyncWorldEdit-1.15" + build; + return "FastAsyncWorldEdit-1.16" + build; } } From fdbef4e12c930d90c790c797d9c28eab17a7f85e Mon Sep 17 00:00:00 2001 From: N0tMyFaultOG Date: Wed, 1 Jul 2020 14:22:04 +0200 Subject: [PATCH 10/11] Update DummyFawe.src --- .../src/main/resources/DummyFawe.src | Bin 2052 -> 2052 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/worldedit-bukkit/src/main/resources/DummyFawe.src b/worldedit-bukkit/src/main/resources/DummyFawe.src index c0bd44906fdc650d680bfea40a15958905c85d57..53af3b0077288d3e919e92ebf5eea14e88e450bf 100644 GIT binary patch delta 1086 zcmV-E1i|}+5QGr05&{T>a^X-tvPHv_8Uh-B+%^n`?*r_A801*&8rT#l(7h#S0`!nv zdnkIKCE8Y378O#O8Ryqm_RP*E+tJ6I2Igauq9p2-9nAQ&{|pj7$l}yr#8!XOCfx02 zFz#uuXzqGibTHijN9;aYaL;$U1F{%O_IHon<8R&LySp8D^KgBfp8lF1#V`@e)Bf*& zPqR0SzrVlVe}ND_{(*=4{hv(kgC}e0ey@qO5Wj!C-+!g>W%&%zhJWsM9(gvh`jn@; z-G0C2;4KCD7J*T357(c@VSVLkA2{O6Lp2$z8Qqoozk@FE5>UUsxDoy0L8N0Guc3d4 zF@Tk~Y?ir^#;p+7_*cN1*@W(wH<^Kdo{f7OjQrKkXbE&VX_f=#Fi{iE5RB>u;`B%A;5NN=s!)ShO5lJE!?Xz8fRSJ7l!fnqOB?Ae zsD|t`9bKs=ujG*a)hVU``3alQ-tcj@jh1?_4>QPf1qlpJ))~)o=#bMCk?YiAjnl{t zsQ3vP8^9DAB{YCO&OU7yX+al;+31!uC-C_Kr-e(rbkS+5F^mmNI$-+L>vEifgBj0x zMJFLPksu14ZU8m5k_t_l=dpo*3=_w++2$FV9RHi9Fi_Y2Ld-7kbIb3h^~^q2pfnh) zAS&{q#!!+J7M7YPbOE^nIS7%#n(wukN3d2Z%#eChjggrsOciLRY7NS}^#U;#`{heY zp|BNRnEd%FXi=SaJ4qed&e1wHU7X#bj)gV5+PXmFXt5y|a;fX8hM`e^3{534u7H69 zyNHoar#{40N>OwTA*HZ*UnxBKLe+loHklzG)Pa|Yf|R91Oj^<#^$aI$VY;Nbp$TM6 z?Lka_bMzc#>Ux{nYdTVG8AB#r@#JI8i88Eg#YHXmA7G+og<@wcDAg zNx_YJm9k~5d^%mW&8ci3Imw1l85#Mt+eoo_1tOcol1;bDGBW_LZNJD^?xV3~rgR0| zfNCa}1DDG~f&dR{QMSSjmPkJg#@b5O;xT`Qt?$=6kml{nPR_-D$-)5&<|7oP)~aWq zFN&HuS$ix>$Q5ygTj!ACZ(8fE zGHblvCvSa63dR?-8nBv7z`4?`+04MT7KTY==_+|`>#)953oz@zdk|{tpUYmoE@)`1@|>k!K^TPkFxE z?e|*_-cpc%BQWai;ri1!tgk%p14n#)s3v1Iqq|c7chDtX0_wLHH=GT-oc>51+@`lq6>9KC2^_Ftm==K>F!D>CvhZzxX(PP_ z)sUU0qbt?qjU3XyI>i(qKVcKv8$Qmq(NYigVFr1rAc4WjI^$Um9deo?a-CYNaT>V+ z6+ay$hOvQ32TY%OU5;~bFylF| z=p@7@5=5cX4WPzWQlUxnJT{PjVd9uJ+dM;)$p zL`6Q-7)p}D!cy~uE+AJR2O%<8^Q{*12-Zr48B%YmF)|Z{sRGSZtwDLWULeL|zkE$8 z6t==ElRsYtEvoZwC#gf*IaelCNt!NI`A@4kg}ABNlSX8Uf_f+OqVn_G=Yq% zJ&4I~j-I1TU2ju+O-HINW5}c{o_wr1QHGVRcxg<#)E&=+_y#8O)`wcq2|}SoUZ8+Z znMxHVG~*O1!-J(3%Tp?UcoJGyF%0`dgow)r2oq#&W19GEdGHjB8>5lz#!+BZX?}7xzcr=&O>4bX zW{ubTW!T5q!16Gp>I9Iwgn;E#)!Z3*}UBwPYVo`>lu>d?`XkpgU)LQ;}4Q%m6 z!*Eq~{{c`-0Rj}0r3M$XLk3#`2uTMvP`1u~7n7z3Fb_36DW|s3)d4j;DW|s3)sy81 EAGbmW Date: Wed, 1 Jul 2020 15:05:38 +0100 Subject: [PATCH 11/11] fix error on loading for some versions --- .../src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index b6a8a7755..6a5327618 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -82,6 +82,8 @@ public class FaweBukkit implements IFawe, Listener { e.printStackTrace(); Bukkit.getServer().shutdown(); } + + chunksStretched = Integer.parseInt(Bukkit.getBukkitVersion().split("-")[0].split("\\.")[1]) >= 16; //Vault is Spigot/Paper only so this needs to be done in the Bukkit module setupVault(); @@ -100,8 +102,6 @@ public class FaweBukkit implements IFawe, Listener { // The tick limiter new ChunkListener_9(); }); - - chunksStretched = Integer.parseInt(Bukkit.getMinecraftVersion().split("\\.")[1]) >= 16; } @Override // Please don't delete this again, it's WIP