Add configurable disallowed states for limits, global disallowed blocks (#1312)

This commit is contained in:
dordsor21
2021-10-09 12:02:26 +01:00
committed by GitHub
parent d641e21dfc
commit 6839fa5567
27 changed files with 575 additions and 48 deletions

View File

@ -50,7 +50,7 @@ import com.fastasyncworldedit.core.math.MutableBlockVector2;
import com.fastasyncworldedit.core.math.MutableBlockVector3;
import com.fastasyncworldedit.core.math.MutableVector3;
import com.fastasyncworldedit.core.math.random.SimplexNoise;
import com.fastasyncworldedit.core.object.FaweLimit;
import com.fastasyncworldedit.core.limit.FaweLimit;
import com.fastasyncworldedit.core.queue.implementation.preloader.Preloader;
import com.fastasyncworldedit.core.regions.RegionWrapper;
import com.fastasyncworldedit.core.util.ExtentTraverser;

View File

@ -23,6 +23,7 @@ import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.configuration.Caption;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.DisallowedBlocksExtent;
import com.fastasyncworldedit.core.extent.FaweRegionExtent;
import com.fastasyncworldedit.core.extent.HistoryExtent;
import com.fastasyncworldedit.core.extent.LimitExtent;
@ -42,7 +43,8 @@ import com.fastasyncworldedit.core.history.RollbackOptimizedHistory;
import com.fastasyncworldedit.core.history.changeset.AbstractChangeSet;
import com.fastasyncworldedit.core.history.changeset.BlockBagChangeSet;
import com.fastasyncworldedit.core.history.changeset.NullChangeSet;
import com.fastasyncworldedit.core.object.FaweLimit;
import com.fastasyncworldedit.core.limit.FaweLimit;
import com.fastasyncworldedit.core.limit.PropertyRemap;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent;
@ -68,7 +70,9 @@ import org.apache.logging.log4j.Logger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
/**
@ -551,6 +555,23 @@ public final class EditSessionBuilder {
this.extent = new StripNBTExtent(this.extent, this.limit.STRIP_NBT);
}
}
if (this.limit != null && !this.limit.isUnlimited()) {
Set<String> limitBlocks = new HashSet<>();
if ((getActor() == null || getActor().hasPermission("worldedit.anyblock") && this.limit.UNIVERSAL_DISALLOWED_BLOCKS)) {
limitBlocks.addAll(WorldEdit.getInstance().getConfiguration().disallowedBlocks);
}
if (this.limit.DISALLOWED_BLOCKS != null && !this.limit.DISALLOWED_BLOCKS.isEmpty()) {
limitBlocks.addAll(this.limit.DISALLOWED_BLOCKS);
}
Set<PropertyRemap<?>> remaps = this.limit.REMAP_PROPERTIES;
if (!limitBlocks.isEmpty() || (remaps != null && !remaps.isEmpty())) {
if (placeChunks) {
extent.addProcessor(new DisallowedBlocksExtent(this.extent, limitBlocks, remaps));
} else {
this.extent = new DisallowedBlocksExtent(this.extent, limitBlocks, remaps);
}
}
}
this.extent = wrapExtent(this.extent, eventBus, event, EditSession.Stage.BEFORE_HISTORY);
}
return this;

View File

@ -27,7 +27,7 @@ import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
import com.fastasyncworldedit.core.history.DiskStorageHistory;
import com.fastasyncworldedit.core.internal.io.FaweInputStream;
import com.fastasyncworldedit.core.internal.io.FaweOutputStream;
import com.fastasyncworldedit.core.object.FaweLimit;
import com.fastasyncworldedit.core.limit.FaweLimit;
import com.fastasyncworldedit.core.util.BrushCache;
import com.fastasyncworldedit.core.util.MainUtil;
import com.fastasyncworldedit.core.util.StringMan;

View File

@ -54,7 +54,7 @@ import com.fastasyncworldedit.core.function.mask.IdMask;
import com.fastasyncworldedit.core.function.mask.SingleBlockTypeMask;
import com.fastasyncworldedit.core.math.heightmap.ScalableHeightMap;
import com.fastasyncworldedit.core.math.heightmap.ScalableHeightMap.Shape;
import com.fastasyncworldedit.core.object.FaweLimit;
import com.fastasyncworldedit.core.limit.FaweLimit;
import com.fastasyncworldedit.core.util.MainUtil;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.StringMan;

View File

@ -29,7 +29,7 @@ import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
import com.fastasyncworldedit.core.extent.clipboard.ReadOnlyClipboard;
import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder;
import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream;
import com.fastasyncworldedit.core.object.FaweLimit;
import com.fastasyncworldedit.core.limit.FaweLimit;
import com.fastasyncworldedit.core.util.ImgurUtility;
import com.fastasyncworldedit.core.util.MainUtil;
import com.fastasyncworldedit.core.util.MaskTraverser;
@ -45,7 +45,6 @@ import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
import com.sk89q.worldedit.command.util.Logging;
import com.sk89q.worldedit.command.util.annotation.Confirm;
import com.sk89q.worldedit.command.util.annotation.Preload;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;

View File

@ -23,7 +23,7 @@ import com.fastasyncworldedit.core.FaweAPI;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.configuration.Caption;
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
import com.fastasyncworldedit.core.object.FaweLimit;
import com.fastasyncworldedit.core.limit.FaweLimit;
import com.fastasyncworldedit.core.util.MaskTraverser;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession;

View File

@ -23,6 +23,8 @@ import com.fastasyncworldedit.core.configuration.Caption;
import com.fastasyncworldedit.core.extent.inventory.SlottableBlockBag;
import com.fastasyncworldedit.core.jnbt.JSON2NBT;
import com.fastasyncworldedit.core.jnbt.NBTException;
import com.fastasyncworldedit.core.limit.FaweLimit;
import com.fastasyncworldedit.core.limit.PropertyRemap;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.StringMan;
import com.fastasyncworldedit.core.world.block.BlanketBaseBlock;
@ -66,6 +68,8 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
@ -171,19 +175,57 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
}
}
private static Map<Property<?>, Object> parseProperties(
//FAWE start - make public
public static Map<Property<?>, Object> parseProperties(
//FAWE end
BlockType type,
String[] stateProperties,
ParserContext context
ParserContext context,
//FAWE start - if null should be returned instead of throwing an error
boolean nullNotError
//FAWE end
) throws NoMatchException {
Map<Property<?>, Object> blockStates = new HashMap<>();
//FAWE start - disallowed states
if (context != null && context.getActor() != null && !context.getActor().getLimit().isUnlimited()) {
for (String input : context.getActor().getLimit().DISALLOWED_BLOCKS) {
if (input.indexOf('[') == -1 && input.indexOf(']') == -1) {
continue;
}
if (!type.getId().equalsIgnoreCase(input.substring(0, input.indexOf('[')))) {
continue;
}
String[] properties = input.substring(input.indexOf('[') + 1, input.indexOf(']')).split(",");
Set<String> blocked = Arrays.stream(properties).filter(s -> {
for (String in : stateProperties) {
if (in.equalsIgnoreCase(s)) {
return true;
}
}
return false;
}).collect(Collectors.toSet());
if (!blocked.isEmpty()) {
throw new DisallowedUsageException(Caption.of(
"fawe.error.limit.disallowed-block",
TextComponent.of(input)
));
}
}
}
//FAWE end
if (stateProperties.length > 0) { // Block data not yet detected
// Parse the block data (optional)
for (String parseableData : stateProperties) {
try {
String[] parts = parseableData.split("=");
if (parts.length != 2) {
//FAWE start - if null should be returned instead of throwing an error
if (nullNotError) {
return null;
}
//FAWE end
throw new InputParseException(
Caption.of(
"worldedit.error.parser.bad-state-format",
@ -195,7 +237,14 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
@SuppressWarnings("unchecked")
Property<Object> propertyKey = (Property<Object>) type.getPropertyMap().get(parts[0]);
if (propertyKey == null) {
if (context.getActor() != null) {
//FAWE start - nullable context
if (context != null && context.getActor() != null) {
//FAWE end
//FAWE start - if null should be returned instead of throwing an error
if (nullNotError) {
return null;
}
//FAWE end
throw new NoMatchException(Caption.of(
"worldedit.error.parser.unknown-property",
TextComponent.of(parts[0]),
@ -207,6 +256,11 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
return Maps.newHashMap();
}
if (blockStates.containsKey(propertyKey)) {
//FAWE start - if null should be returned instead of throwing an error
if (nullNotError) {
return null;
}
//FAWE end
throw new InputParseException(Caption.of(
"worldedit.error.parser.duplicate-property",
TextComponent.of(parts[0])
@ -216,6 +270,11 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
try {
value = propertyKey.getValueFor(parts[1]);
} catch (IllegalArgumentException e) {
//FAWE start - if null should be returned instead of throwing an error
if (nullNotError) {
return null;
}
//FAWE end
throw new NoMatchException(Caption.of(
"worldedit.error.parser.unknown-value",
TextComponent.of(parts[1]),
@ -223,10 +282,30 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
));
}
//FAWE start - blocked states
if (context != null && context.getActor() != null && !context.getActor().getLimit().isUnlimited()) {
if (context.getActor().getLimit().REMAP_PROPERTIES != null
&& !context.getActor().getLimit().REMAP_PROPERTIES.isEmpty()) {
for (PropertyRemap remap : context.getActor().getLimit().REMAP_PROPERTIES) {
Object newValue = remap.apply(type, value);
if (newValue != value) {
value = newValue;
break;
}
}
}
}
//FAWE end
blockStates.put(propertyKey, value);
} catch (NoMatchException e) {
} catch (NoMatchException | DisallowedUsageException e) {
throw e; // Pass-through
} catch (Exception e) {
//FAWE start - if null should be returned instead of throwing an error
if (nullNotError) {
return null;
}
//FAWE end
throw new InputParseException(Caption.of(
"worldedit.error.parser.bad-state-format",
TextComponent.of(parseableData)
@ -387,7 +466,9 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
}
//FAWE end
blockStates.putAll(parseProperties(state.getBlockType(), stateProperties, context));
//FAWE start - Not null if nullNotError false.
blockStates.putAll(parseProperties(state.getBlockType(), stateProperties, context, false));
//FAWE end
if (context.isPreferringWildcard()) {
if (stateString == null || stateString.isEmpty()) {
state = new FuzzyBlockState(state);
@ -428,10 +509,23 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
if (context.isRestricted()) {
Actor actor = context.requireActor();
if (actor != null && !actor.hasPermission("worldedit.anyblock")
&& worldEdit.getConfiguration().disallowedBlocks.contains(blockType.getId())) {
throw new DisallowedUsageException(Caption.of("worldedit.error.disallowed-block", TextComponent.of(input)));
//FAWE start - per-limit disallowed blocks
if (actor != null) {
if (!actor.hasPermission("worldedit.anyblock")
&& worldEdit.getConfiguration().disallowedBlocks.contains(blockType.getId().toLowerCase(Locale.ROOT))) {
throw new DisallowedUsageException(Caption.of("worldedit.error.disallowed-block", TextComponent.of(blockType.getId())));
}
FaweLimit limit = actor.getLimit();
if (!limit.isUnlimited()) {
// No need to account for blocked states/properties as it will simply return false in the equality check
// during contains.
if (limit.DISALLOWED_BLOCKS.contains(blockType.getId().toLowerCase(Locale.ROOT))) {
throw new DisallowedUsageException(Caption.of("fawe.error.limit.disallowed-block",
TextComponent.of(blockType.getId())));
}
}
}
//FAWE end
}
if (nbt != null) {

View File

@ -22,7 +22,7 @@ package com.sk89q.worldedit.extension.platform;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.entity.MapMetadatable;
import com.fastasyncworldedit.core.object.FaweLimit;
import com.fastasyncworldedit.core.limit.FaweLimit;
import com.fastasyncworldedit.core.util.task.InterruptableCondition;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.internal.cui.CUIEvent;

View File

@ -22,7 +22,7 @@ package com.sk89q.worldedit.regions;
import com.fastasyncworldedit.core.extent.SingleRegionExtent;
import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock;
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
import com.fastasyncworldedit.core.object.FaweLimit;
import com.fastasyncworldedit.core.limit.FaweLimit;
import com.fastasyncworldedit.core.queue.Filter;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunk;

View File

@ -19,10 +19,12 @@
package com.sk89q.worldedit.registry.state;
import com.fastasyncworldedit.core.limit.PropertyRemap;
import com.fastasyncworldedit.core.registry.state.PropertyKey;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Objects;
/**
* Describes a state property of a block.
@ -57,16 +59,45 @@ public interface Property<T> {
T getValueFor(String string) throws IllegalArgumentException;
//FAWE start
/**
* Get the index of the given value in the list of values
*
* @param value value to get index for
* @throws IllegalArgumentException if value not applicable to this property
*/
default int getIndex(T value) {
return getValues().indexOf(value);
}
default int getIndexFor(CharSequence string) throws IllegalArgumentException {
return getIndex(getValueFor(string.toString()));
/**
* Get the index of the given value in the list of values
*
* @param value value to get index for
* @throws IllegalArgumentException if value not applicable to this property
*/
default int getIndexFor(CharSequence value) throws IllegalArgumentException {
return getIndex(getValueFor(value.toString()));
}
/**
* Get the {@link PropertyKey} associated with this property.
*/
default PropertyKey getKey() {
return PropertyKey.getOrCreate(getName());
}
/**
* Get a {@link PropertyRemap} instance for this property with the given remap.
*
* @param from value to remap from
* @param to value to remap to
* @return new {@link PropertyRemap} instance
*/
default PropertyRemap<T> getRemap(Object from, Object to) {
Objects.requireNonNull(from);
Objects.requireNonNull(to);
return new PropertyRemap<T>(this, (T) from, (T) to);
}
//FAWE end
}

View File

@ -42,6 +42,7 @@ import java.util.HashSet;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Simple LocalConfiguration that loads settings using
@ -92,7 +93,10 @@ public class PropertiesConfiguration extends LocalConfiguration {
profile = getBool("profile", profile);
traceUnflushedSessions = getBool("trace-unflushed-sessions", traceUnflushedSessions);
disallowedBlocks = getStringSet("disallowed-blocks", getDefaultDisallowedBlocks());
disallowedBlocks = getStringSet("disallowed-blocks", getDefaultDisallowedBlocks())
.stream()
.map(s -> s.contains(":") ? s.toLowerCase(Locale.ROOT) : ("minecraft:" + s).toLowerCase(Locale.ROOT))
.collect(Collectors.toSet());
defaultChangeLimit = getInt("default-max-changed-blocks", defaultChangeLimit);
maxChangeLimit = getInt("max-changed-blocks", maxChangeLimit);
defaultVerticalHeight = getInt("default-vertical-height", defaultVerticalHeight);

View File

@ -30,6 +30,7 @@ import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.HashSet;
import java.util.Locale;
import java.util.stream.Collectors;
/**
* A less simple implementation of {@link LocalConfiguration}
@ -94,10 +95,10 @@ public class YAMLConfiguration extends LocalConfiguration {
butcherDefaultRadius = Math.max(-1, config.getInt("limits.butcher-radius.default", butcherDefaultRadius));
butcherMaxRadius = Math.max(-1, config.getInt("limits.butcher-radius.maximum", butcherMaxRadius));
disallowedBlocks = new HashSet<>(config.getStringList(
"limits.disallowed-blocks",
Lists.newArrayList(getDefaultDisallowedBlocks())
));
disallowedBlocks = config.getStringList("limits.disallowed-blocks", Lists.newArrayList(getDefaultDisallowedBlocks()))
.stream()
.map(s -> s.contains(":") ? s.toLowerCase(Locale.ROOT) : ("minecraft:" + s).toLowerCase(Locale.ROOT))
.collect(Collectors.toSet());
allowedDataCycleBlocks = new HashSet<>(config.getStringList("limits.allowed-data-cycle-blocks", null));
registerHelp = config.getBoolean("register-help", true);

View File

@ -152,8 +152,8 @@ public class BlockState implements BlockStateHolder<BlockState>, Pattern {
String input = key.toString();
throw new SuggestInputParseException("Does not match a valid block type: " + input, input, () -> Stream.of(
BlockTypesCache.values)
.filter(b -> StringMan.blockStateMatches(input, b.getId()))
.map(BlockType::getId)
.filter(id -> StringMan.blockStateMatches(input, id))
.sorted(StringMan.blockStateComparator(input))
.collect(Collectors.toList())
);
@ -163,7 +163,7 @@ public class BlockState implements BlockStateHolder<BlockState>, Pattern {
return type.getDefaultState();
}
List<? extends Property> propList = type.getProperties();
List<? extends Property<?>> propList = type.getProperties();
if (state.charAt(state.length() - 1) != ']') {
state = state + "]";
@ -172,7 +172,7 @@ public class BlockState implements BlockStateHolder<BlockState>, Pattern {
charSequence.setString(state);
if (propList.size() == 1) {
AbstractProperty property = (AbstractProperty) propList.get(0);
AbstractProperty<?> property = (AbstractProperty<?>) propList.get(0);
String name = property.getName();
charSequence.setSubstring(propStrStart + name.length() + 2, state.length() - 1);
@ -188,7 +188,7 @@ public class BlockState implements BlockStateHolder<BlockState>, Pattern {
stateId = type.getDefaultState().getInternalId();
}
int length = state.length();
AbstractProperty property = null;
AbstractProperty<?> property = null;
int last = propStrStart + 1;
for (int i = last; i < length; i++) {
@ -200,7 +200,7 @@ public class BlockState implements BlockStateHolder<BlockState>, Pattern {
if (property != null) {
int index = property.getIndexFor(charSequence);
if (index == -1) {
throw SuggestInputParseException.of(charSequence.toString(), property.getValues());
throw SuggestInputParseException.of(charSequence.toString(), (List<Object>) property.getValues());
}
stateId = property.modifyIndex(stateId, index);
} else {
@ -288,7 +288,7 @@ public class BlockState implements BlockStateHolder<BlockState>, Pattern {
public <V> BlockState with(final Property<V> property, final V value) {
try {
BlockType type = getBlockType();
int newState = ((AbstractProperty) property).modify(this.getInternalId(), value);
int newState = ((AbstractProperty<V>) property).modify(this.getInternalId(), value);
return newState != this.getInternalId() ? type.withStateId(newState) : this;
} catch (ClassCastException e) {
throw new IllegalArgumentException("Property not found: " + property);
@ -298,7 +298,7 @@ public class BlockState implements BlockStateHolder<BlockState>, Pattern {
@Override
public <V> V getState(final Property<V> property) {
try {
AbstractProperty ap = (AbstractProperty) property;
AbstractProperty<V> ap = (AbstractProperty<V>) property;
return (V) ap.getValue(this.getInternalId());
} catch (ClassCastException e) {
throw new IllegalArgumentException("Property not found: " + property);
@ -309,7 +309,11 @@ public class BlockState implements BlockStateHolder<BlockState>, Pattern {
public <V> BlockState with(final PropertyKey property, final V value) {
try {
BlockType type = getBlockType();
int newState = ((AbstractProperty) type.getProperty(property)).modify(this.getInternalId(), value);
AbstractProperty<V> abstractProperty = ((AbstractProperty<V>) type.getProperty(property));
if (abstractProperty == null) {
return this;
}
int newState = abstractProperty.modify(this.getInternalId(), value);
return newState != this.getInternalId() ? type.withStateId(newState) : this;
} catch (ClassCastException e) {
throw new IllegalArgumentException("Property not found: " + property);
@ -340,7 +344,7 @@ public class BlockState implements BlockStateHolder<BlockState>, Pattern {
//FAWE end
BlockType type = this.getBlockType();
// Lazily initialize the map
Map<? extends Property, Object> map = Maps.asMap(type.getPropertiesSet(), (Function<Property, Object>) this::getState);
Map<? extends Property<?>, Object> map = Maps.asMap(type.getPropertiesSet(), (Function<Property<?>, Object>) this::getState);
//noinspection RedundantCast - This is required for compilation, etc.
return Collections.unmodifiableMap((Map<Property<?>, Object>) map);
//FAWE end

View File

@ -52,8 +52,8 @@ public class FuzzyBlockState extends BlockState {
}
//FAWE end
private FuzzyBlockState(BlockState state, Map<Property<?>, Object> values) {
//FAWE start - use internal ids
//FAWE start - use internal ids, public constructor
public FuzzyBlockState(BlockState state, Map<Property<?>, Object> values) {
super(state.getBlockType(), state.getInternalId(), state.getOrdinal());
if (values == null || values.isEmpty()) {
props = Collections.emptyMap();