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.
This commit is contained in:
Matthew Miller 2020-03-08 16:09:36 +10:00 committed by MattBDev
parent 68679e007e
commit 4604aa5920
39 changed files with 877 additions and 176 deletions

View File

@ -68,6 +68,6 @@
</module>
<!-- Validate that command annotations are formatted correctly -->
<module name="RegexpMultiline">
<property name="format" value="^( +)@(Arg|Switch|Command)\(.*?\n\1 {5,}"/>
<property name="format" value="^( +)@(Arg|Switch|Command)\(.*?\r?\n\1 {5,}"/>
</module>
</module>

View File

@ -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<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
SideEffect.NEIGHBORS
);
@Override
public Set<SideEffect> getSupportedSideEffects() {
if (plugin.getBukkitImplAdapter() != null) {
return plugin.getBukkitImplAdapter().getSupportedSideEffects();
}
return SUPPORTED_SIDE_EFFECTS;
}
public void unregisterCommands() {
dynamicCommands.unregisterCommands();
}

View File

@ -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 <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException {
public <B extends BlockStateHolder<B>> 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<SideEffect> 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

View File

@ -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<T> 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
*/
<B extends BlockStateHolder<B>> 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<T> extends IBukkitAdapter {
*/
BaseItemStack adapt(ItemStack itemStack);
/**
* Get the {@link SideEffect}s that this adapter supports.
*
* @return The side effects that are supported
*/
Set<SideEffect> getSupportedSideEffects();
default OptionalInt getInternalBlockStateId(BlockData data) {
// return OptionalInt.empty();
return getInternalBlockStateId(BukkitAdapter.adapt(data));

View File

@ -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);
}
}
}

View File

@ -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<T> extends BukkitImplAdapter<T> {
}
default <B extends BlockStateHolder<B>> 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

View File

@ -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<SideEffect> getSupportedSideEffects() {
return SideEffectSet.defaults().getSideEffectsToApply();
}
@Override
public <B extends BlockStateHolder<B>> 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

View File

@ -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<SideEffect> getSupportedSideEffects() {
return SideEffectSet.defaults().getSideEffectsToApply();
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(Location location, B state, boolean notifyAndLight) {
return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, notifyAndLight);
}
public <B extends BlockStateHolder<B>> 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

View File

@ -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<SideEffect> getSupportedSideEffects() {
return SideEffectSet.defaults().getSideEffectsToApply();
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(Location location, B state, boolean notifyAndLight) {
return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, notifyAndLight);
}
public <B extends BlockStateHolder<B>> 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

View File

@ -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<SideEffect> getSupportedSideEffects() {
return ImmutableSet.of();
}
public void addWorld(World world) {
worlds.add(world);
}

View File

@ -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 <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight)
throws WorldEditException {
public <B extends BlockStateHolder<B>> 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<SideEffect> applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException {
return ImmutableSet.of();
}
@Override

View File

@ -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 <B extends BlockStateHolder<B>> 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<SideEffect> applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException {
return SideEffectSet.none().getSideEffectsToApply();
}
@Override
public boolean clearContainerBlockContents(BlockVector3 position) {
return false;

View File

@ -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<SideEffect> applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet)
throws WorldEditException{
return SideEffectSet.none().getSideEffectsToApply();
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block)
throws WorldEditException {
return setBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ(), block);
}
@Override
public <B extends BlockStateHolder<B>> 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()) {

View File

@ -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<SideEffect> applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet)
throws WorldEditException{
return parent.applySideEffects(position, previousType, sideEffectSet);
}
@Override
public <B extends BlockStateHolder<B>> 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 <B extends BlockStateHolder<B>> 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);

View File

@ -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)
*

View File

@ -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();
}
/**

View File

@ -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));
}
}

View File

@ -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),

View File

@ -0,0 +1,75 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<SideEffect> {
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<SideEffect> getSideEffects() {
return WorldEdit.getInstance().getPlatformManager().getSupportedSideEffects();
}
@Override
public Component describeAcceptableArguments() {
return choices;
}
@Override
public List<String> getSuggestions(String input, InjectedValueAccess context) {
return limitByPrefix(getSideEffects().stream().map(sideEffect -> sideEffect.name().toLowerCase(Locale.US)), input);
}
@Override
public ConversionResult<SideEffect> convert(String argument, InjectedValueAccess context) {
try {
return SuccessfulConversion.fromSingle(SideEffect.valueOf(argument.toUpperCase(Locale.US)));
} catch (Exception e) {
return FailedConversion.from(e);
}
}
}

View File

@ -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<Capability, Preference> getCapabilities();
Set<SideEffect> getSupportedSideEffects();
}

View File

@ -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));

View File

@ -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<SideEffect> getSupportedSideEffects() {
return queryCapability(Capability.WORLD_EDITING).getSupportedSideEffects();
}
@Subscribe
public void handlePlatformReady(PlatformReadyEvent event) {
choosePreferred();

View File

@ -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<BlockVector3, BaseBlock> buffer = Maps.newHashMap();
private final Map<BlockVector3, BaseBlock> buffer = BlockMap.create();
private final Mask mask;
/**

View File

@ -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;

View File

@ -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<BlockVector3> positions = new HashSet<>();
private final Map<BlockVector3, BlockState> positions = BlockMap.create();
private final Set<BlockVector2> 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 <B extends BlockStateHolder<B>> 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<BlockVector3> positionIterator = positions.iterator();
if (postEditSimulation) {
Iterator<Map.Entry<BlockVector3, BlockState>> positionIterator = positions.entrySet().iterator();
while (run.shouldContinue() && positionIterator.hasNext()) {
BlockVector3 position = positionIterator.next();
world.notifyAndLightBlock(position, BlockTypes.AIR.getDefaultState());
Map.Entry<BlockVector3, BlockState> position = positionIterator.next();
world.applySideEffects(position.getKey(), position.getValue(), sideEffectSet);
positionIterator.remove();
}
@ -151,5 +124,4 @@ public class FastModeExtent extends AbstractDelegateExtent {
}
};
}
}

View File

@ -0,0 +1,68 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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;
}
}
}

View File

@ -0,0 +1,94 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<SideEffect, SideEffect.State> sideEffects;
private final Set<SideEffect> appliedSideEffects;
private final boolean appliesAny;
private SideEffectSet() {
this(ImmutableMap.of());
}
public SideEffectSet(Map<SideEffect, SideEffect.State> 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<SideEffect, SideEffect.State> 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<SideEffect> getSideEffectsToApply() {
return this.appliedSideEffects;
}
public static SideEffectSet defaults() {
return DEFAULT;
}
public static SideEffectSet none() {
return NONE;
}
}

View File

@ -0,0 +1,88 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<SideEffect> sideEffects;
private SideEffectSet sideEffectSet;
private static List<SideEffect> 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();
}
}

View File

@ -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 <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 pt, B block) throws WorldEditException {
return setBlock(pt, block, true);
return setBlock(pt, block, SideEffectSet.defaults());
}
@Override

View File

@ -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 <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException {
public <B extends BlockStateHolder<B>> 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<SideEffect> applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet)
throws WorldEditException {
return ImmutableSet.of();
}
@Override

View File

@ -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<IChunkGet> {
* @param notifyAndLight true to to notify and light
* @return true if the block was successfully set (return value may not be accurate)
*/
@Deprecated
default <B extends BlockStateHolder<B>> 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.
*
* <p>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.</p>
*
* @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)
*/
<B extends BlockStateHolder<B>> 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<IChunkGet> {
* @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<SideEffect> applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException;
/**
* Get the light level at the given block.

View File

@ -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.",

View File

@ -61,16 +61,12 @@ tasks.named<Copy>("processResources") {
}
}
<<<<<<< HEAD
addJarManifest(includeClasspath = true)
=======
tasks.named<Jar>("jar") {
manifest {
attributes("Class-Path" to CLASSPATH,
"WorldEdit-Version" to project.version)
}
}
>>>>>>> 18a55bc14... Add new experimental snapshot API (#524)
tasks.named<ShadowJar>("shadowJar") {
archiveClassifier.set("dist-dev")

View File

@ -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<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
SideEffect.CONNECTIONS,
SideEffect.ENTITY_AI,
SideEffect.LIGHTING,
SideEffect.NEIGHBORS
);
@Override
public Set<SideEffect> getSupportedSideEffects() {
return SUPPORTED_SIDE_EFFECTS;
}
@Override
public Collection<Actor> getConnectedUsers() {
List<Actor> users = new ArrayList<>();

View File

@ -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 <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException {
public <B extends BlockStateHolder<B>> 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<SideEffect> 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

View File

@ -77,7 +77,12 @@ tasks.named<Copy>("processResources") {
}
}
addJarManifest(includeClasspath = false)
tasks.named<Jar>("jar") {
manifest {
attributes("Class-Path" to CLASSPATH,
"WorldEdit-Version" to project.version)
}
}
tasks.named<ShadowJar>("shadowJar") {
dependencies {

View File

@ -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<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
SideEffect.CONNECTIONS,
SideEffect.ENTITY_AI,
SideEffect.LIGHTING,
SideEffect.NEIGHBORS
);
@Override
public Set<SideEffect> getSupportedSideEffects() {
return SUPPORTED_SIDE_EFFECTS;
}
@Override
public Collection<Actor> getConnectedUsers() {
List<Actor> users = new ArrayList<>();

View File

@ -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<World> 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 <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException {
public <B extends BlockStateHolder<B>> 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<SideEffect> 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

View File

@ -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<SideEffect> getSupportedSideEffects() {
return ImmutableSet.of();
}
@Override
public Collection<Actor> getConnectedUsers() {
List<Actor> users = new ArrayList<>();