geforkt von Mirrors/FastAsyncWorldEdit
fix: fix regex block masking (#2242)
Dieser Commit ist enthalten in:
Ursprung
88533118bc
Commit
d961aa91bc
@ -20,12 +20,11 @@ import com.sk89q.worldedit.world.block.BlockType;
|
|||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
|
|
||||||
import java.util.AbstractMap;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -36,10 +35,12 @@ import java.util.concurrent.Executors;
|
|||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.function.BiPredicate;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
public class BlockMaskBuilder {
|
public class BlockMaskBuilder {
|
||||||
|
|
||||||
private static final Operator GREATER = (a, b) -> a > b;
|
private static final Operator GREATER = (a, b) -> a > b;
|
||||||
@ -63,58 +64,97 @@ public class BlockMaskBuilder {
|
|||||||
this.bitSets = bitSets;
|
this.bitSets = bitSets;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean filterRegex(BlockType blockType, PropertyKey key, String regex) {
|
private boolean handleRegex(BlockType blockType, PropertyKey key, String regex, FuzzyStateAllowingBuilder builder) {
|
||||||
Property<Object> property = blockType.getProperty(key);
|
Property<Object> property = blockType.getProperty(key);
|
||||||
if (property == null) {
|
if (property == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
List<Object> values = property.getValues();
|
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
|
List<Object> values = property.getValues();
|
||||||
for (int i = 0; i < values.size(); i++) {
|
for (int i = 0; i < values.size(); i++) {
|
||||||
Object value = values.get(i);
|
if (values.get(i).toString().matches(regex)) {
|
||||||
if (!value.toString().matches(regex) && has(blockType, property, i)) {
|
builder.allow(property, i);
|
||||||
filter(blockType, property, i);
|
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean filterOperator(BlockType blockType, PropertyKey key, Operator operator, CharSequence value) {
|
private boolean handleOperator(
|
||||||
|
BlockType blockType,
|
||||||
|
PropertyKey key,
|
||||||
|
Operator operator,
|
||||||
|
CharSequence stringValue,
|
||||||
|
FuzzyStateAllowingBuilder builder
|
||||||
|
) {
|
||||||
Property<Object> property = blockType.getProperty(key);
|
Property<Object> property = blockType.getProperty(key);
|
||||||
if (property == null) {
|
if (property == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int index = property.getIndexFor(value);
|
int index = property.getIndexFor(stringValue);
|
||||||
List<Object> values = property.getValues();
|
List<Object> values = property.getValues();
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
for (int i = 0; i < values.size(); i++) {
|
for (int i = 0; i < values.size(); i++) {
|
||||||
if (!operator.test(index, i) && has(blockType, property, i)) {
|
if (operator.test(index, i)) {
|
||||||
filter(blockType, property, i);
|
builder.allow(property, i);
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean filterRegexOrOperator(BlockType type, PropertyKey key, Operator operator, CharSequence value) {
|
private boolean handleRegexOrOperator(
|
||||||
boolean result = false;
|
BlockType type,
|
||||||
if (!type.hasProperty(key)) {
|
PropertyKey key,
|
||||||
if (operator == EQUAL) {
|
Operator operator,
|
||||||
result = bitSets[type.getInternalId()] != null;
|
CharSequence value,
|
||||||
remove(type);
|
FuzzyStateAllowingBuilder builder
|
||||||
|
) {
|
||||||
|
if (!type.hasProperty(key) && operator == EQUAL) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (value.length() == 0) {
|
if (value.length() == 0) {
|
||||||
return result;
|
return false;
|
||||||
}
|
}
|
||||||
if ((operator == EQUAL || operator == EQUAL_OR_NULL) && !StringMan.isAlphanumericUnd(value)) {
|
if ((operator == EQUAL || operator == EQUAL_OR_NULL) && !StringMan.isAlphanumericUnd(value)) {
|
||||||
result = filterRegex(type, key, value.toString());
|
return handleRegex(type, key, value.toString(), builder);
|
||||||
} else {
|
} else {
|
||||||
result = filterOperator(type, key, operator, value);
|
return handleOperator(type, key, operator, value, builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(FuzzyStateAllowingBuilder builder) {
|
||||||
|
long[] states = bitSets[builder.getType().getInternalId()];
|
||||||
|
if (states == ALL) {
|
||||||
|
bitSets[builder.getType().getInternalId()] = states = FastBitSet.create(builder.getType().getMaxStateId() + 1);
|
||||||
|
FastBitSet.unsetAll(states);
|
||||||
|
}
|
||||||
|
applyRecursive(0, builder.getType().getInternalId(), builder, states);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyRecursive(
|
||||||
|
int propertiesIndex,
|
||||||
|
int state,
|
||||||
|
FuzzyStateAllowingBuilder builder,
|
||||||
|
long[] states
|
||||||
|
) {
|
||||||
|
AbstractProperty<?> current = (AbstractProperty<?>) builder.getType().getProperties().get(propertiesIndex);
|
||||||
|
List<?> values = current.getValues();
|
||||||
|
if (propertiesIndex + 1 < builder.getType().getProperties().size()) {
|
||||||
|
for (int i = 0; i < values.size(); i++) {
|
||||||
|
if (builder.allows(current) || builder.allows(current, i)) {
|
||||||
|
int newState = current.modifyIndex(state, i);
|
||||||
|
applyRecursive(propertiesIndex + 1, newState, builder, states);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < values.size(); i++) {
|
||||||
|
if (builder.allows(current) || builder.allows(current, i)) {
|
||||||
|
int index = current.modifyIndex(state, i) >> BlockTypesCache.BIT_OFFSET;
|
||||||
|
FastBitSet.set(states, index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockMaskBuilder addRegex(final String input) throws InputParseException {
|
public BlockMaskBuilder addRegex(final String input) throws InputParseException {
|
||||||
@ -130,26 +170,28 @@ public class BlockMaskBuilder {
|
|||||||
charSequence.setString(input);
|
charSequence.setString(input);
|
||||||
charSequence.setSubstring(0, propStart);
|
charSequence.setSubstring(0, propStart);
|
||||||
|
|
||||||
BlockType type = null;
|
List<BlockType> blockTypeList;
|
||||||
List<BlockType> blockTypeList = null;
|
List<FuzzyStateAllowingBuilder> builders;
|
||||||
if (StringMan.isAlphanumericUnd(charSequence)) {
|
if (StringMan.isAlphanumericUnd(charSequence)) {
|
||||||
type = BlockTypes.parse(charSequence.toString());
|
BlockType type = BlockTypes.parse(charSequence.toString());
|
||||||
|
blockTypeList = Collections.singletonList(type);
|
||||||
|
builders = Collections.singletonList(new FuzzyStateAllowingBuilder(type));
|
||||||
add(type);
|
add(type);
|
||||||
} else {
|
} else {
|
||||||
String regex = charSequence.toString();
|
String regex = charSequence.toString();
|
||||||
blockTypeList = new ArrayList<>();
|
blockTypeList = new ArrayList<>();
|
||||||
for (BlockType myType : BlockTypesCache.values) {
|
builders = new ArrayList<>();
|
||||||
if (myType.getId().matches(regex)) {
|
Pattern pattern = Pattern.compile("(minecraft:)?" + regex);
|
||||||
blockTypeList.add(myType);
|
for (BlockType type : BlockTypesCache.values) {
|
||||||
add(myType);
|
if (pattern.matcher(type.getId()).find()) {
|
||||||
|
blockTypeList.add(type);
|
||||||
|
builders.add(new FuzzyStateAllowingBuilder(type));
|
||||||
|
add(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (blockTypeList.isEmpty()) {
|
if (blockTypeList.isEmpty()) {
|
||||||
throw new InputParseException(Caption.of("fawe.error.no-block-found", TextComponent.of(input)));
|
throw new InputParseException(Caption.of("fawe.error.no-block-found", TextComponent.of(input)));
|
||||||
}
|
}
|
||||||
if (blockTypeList.size() == 1) {
|
|
||||||
type = blockTypeList.get(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Empty string
|
// Empty string
|
||||||
charSequence.setSubstring(0, 0);
|
charSequence.setSubstring(0, 0);
|
||||||
@ -169,11 +211,11 @@ public class BlockMaskBuilder {
|
|||||||
}
|
}
|
||||||
case ']', ',' -> {
|
case ']', ',' -> {
|
||||||
charSequence.setSubstring(last, i);
|
charSequence.setSubstring(last, i);
|
||||||
if (key == null && PropertyKey.getByName(charSequence) == null) {
|
if (key == null && (key = PropertyKey.getByName(charSequence)) == null) {
|
||||||
suggest(
|
suggest(
|
||||||
input,
|
input,
|
||||||
charSequence.toString(),
|
charSequence.toString(),
|
||||||
type != null ? Collections.singleton(type) : blockTypeList
|
blockTypeList
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (operator == null) {
|
if (operator == null) {
|
||||||
@ -182,34 +224,20 @@ public class BlockMaskBuilder {
|
|||||||
() -> Arrays.asList("=", "~", "!", "<", ">", "<=", ">=")
|
() -> Arrays.asList("=", "~", "!", "<", ">", "<=", ">=")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
boolean filtered = false;
|
for (int index = 0; index < blockTypeList.size(); index++) {
|
||||||
if (type != null) {
|
if (!handleRegexOrOperator(
|
||||||
filtered = filterRegexOrOperator(type, key, operator, charSequence);
|
blockTypeList.get(index),
|
||||||
} else {
|
key,
|
||||||
for (BlockType myType : blockTypeList) {
|
operator,
|
||||||
filtered |= filterRegexOrOperator(myType, key, operator, charSequence);
|
charSequence,
|
||||||
|
builders.get(index)
|
||||||
|
)) {
|
||||||
|
// If we cannot find a matching property for all to mask, do not mask the block
|
||||||
|
blockTypeList.remove(index);
|
||||||
|
builders.remove(index);
|
||||||
|
index--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!filtered) {
|
|
||||||
String value = charSequence.toString();
|
|
||||||
final PropertyKey fKey = key;
|
|
||||||
Collection<BlockType> types = type != null ? Collections.singleton(type) : blockTypeList;
|
|
||||||
throw new SuggestInputParseException(Caption.of("fawe.error.no-value-for-input", input), () -> {
|
|
||||||
HashSet<String> values = new HashSet<>();
|
|
||||||
types.stream().filter(t -> t.hasProperty(fKey)).forEach(t -> {
|
|
||||||
Property<Object> p = t.getProperty(fKey);
|
|
||||||
for (int j = 0; j < p.getValues().size(); j++) {
|
|
||||||
if (has(t, p, j)) {
|
|
||||||
String o = p.getValues().get(j).toString();
|
|
||||||
if (o.startsWith(value)) {
|
|
||||||
values.add(o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return new ArrayList<>(values);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Reset state
|
// Reset state
|
||||||
key = null;
|
key = null;
|
||||||
operator = null;
|
operator = null;
|
||||||
@ -235,7 +263,7 @@ public class BlockMaskBuilder {
|
|||||||
suggest(
|
suggest(
|
||||||
input,
|
input,
|
||||||
charSequence.toString(),
|
charSequence.toString(),
|
||||||
type != null ? Collections.singleton(type) : blockTypeList
|
blockTypeList
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,13 +273,18 @@ public class BlockMaskBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (FuzzyStateAllowingBuilder builder : builders) {
|
||||||
|
if (builder.allows()) {
|
||||||
|
add(builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (StringMan.isAlphanumericUnd(input)) {
|
if (StringMan.isAlphanumericUnd(input)) {
|
||||||
add(BlockTypes.parse(input));
|
add(BlockTypes.parse(input));
|
||||||
} else {
|
} else {
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
for (BlockType myType : BlockTypesCache.values) {
|
for (BlockType myType : BlockTypesCache.values) {
|
||||||
if (myType.getId().matches(input)) {
|
if (myType.getId().matches("(minecraft:)?" + input)) {
|
||||||
add(myType);
|
add(myType);
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
@ -275,16 +308,6 @@ public class BlockMaskBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> boolean has(BlockType type, Property<T> property, int index) {
|
|
||||||
AbstractProperty<T> prop = (AbstractProperty<T>) property;
|
|
||||||
long[] states = bitSets[type.getInternalId()];
|
|
||||||
if (states == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int localI = index << prop.getBitOffset() >> BlockTypesCache.BIT_OFFSET;
|
|
||||||
return (states == ALL || FastBitSet.get(states, localI));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void suggest(String input, String property, Collection<BlockType> finalTypes) throws InputParseException {
|
private void suggest(String input, String property, Collection<BlockType> finalTypes) throws InputParseException {
|
||||||
throw new SuggestInputParseException(Caption.of("worldedit.error.parser.unknown-property", property, input), () -> {
|
throw new SuggestInputParseException(Caption.of("worldedit.error.parser.unknown-property", property, input), () -> {
|
||||||
Set<PropertyKey> keys = PropertyKeySet.empty();
|
Set<PropertyKey> keys = PropertyKeySet.empty();
|
||||||
@ -381,42 +404,6 @@ public class BlockMaskBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
|
||||||
public <T> BlockMaskBuilder filter(
|
|
||||||
Predicate<BlockType> typePredicate,
|
|
||||||
BiPredicate<BlockType, Map.Entry<Property<T>, T>> allowed
|
|
||||||
) {
|
|
||||||
for (int i = 0; i < bitSets.length; i++) {
|
|
||||||
long[] states = bitSets[i];
|
|
||||||
if (states == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
BlockType type = BlockTypes.get(i);
|
|
||||||
if (!typePredicate.test(type)) {
|
|
||||||
bitSets[i] = null;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
List<AbstractProperty<?>> properties = (List<AbstractProperty<?>>) type.getProperties();
|
|
||||||
for (AbstractProperty<?> prop : properties) {
|
|
||||||
List<?> values = prop.getValues();
|
|
||||||
for (int j = 0; j < values.size(); j++) {
|
|
||||||
int localI = j << prop.getBitOffset() >> BlockTypesCache.BIT_OFFSET;
|
|
||||||
if (states == ALL || FastBitSet.get(states, localI)) {
|
|
||||||
if (!allowed.test(type, new AbstractMap.SimpleEntry(prop, values.get(j)))) {
|
|
||||||
if (states == ALL) {
|
|
||||||
bitSets[i] = states = FastBitSet.create(type.getMaxStateId() + 1);
|
|
||||||
FastBitSet.setAll(states);
|
|
||||||
}
|
|
||||||
FastBitSet.clear(states, localI);
|
|
||||||
reset(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockMaskBuilder add(BlockType type) {
|
public BlockMaskBuilder add(BlockType type) {
|
||||||
bitSets[type.getInternalId()] = ALL;
|
bitSets[type.getInternalId()] = ALL;
|
||||||
return this;
|
return this;
|
||||||
@ -478,117 +465,6 @@ public class BlockMaskBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
|
||||||
public BlockMaskBuilder addAll(
|
|
||||||
Predicate<BlockType> typePredicate,
|
|
||||||
BiPredicate<BlockType, Map.Entry<Property<?>, ?>> propPredicate
|
|
||||||
) {
|
|
||||||
for (int i = 0; i < bitSets.length; i++) {
|
|
||||||
long[] states = bitSets[i];
|
|
||||||
if (states == ALL) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
BlockType type = BlockTypes.get(i);
|
|
||||||
if (!typePredicate.test(type)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (AbstractProperty<?> prop : (List<AbstractProperty<?>>) type.getProperties()) {
|
|
||||||
List<?> values = prop.getValues();
|
|
||||||
for (int j = 0; j < values.size(); j++) {
|
|
||||||
int localI = j << prop.getBitOffset() >> BlockTypesCache.BIT_OFFSET;
|
|
||||||
if (states == null || !FastBitSet.get(states, localI)) {
|
|
||||||
if (propPredicate.test(type, new AbstractMap.SimpleEntry(prop, values.get(j)))) {
|
|
||||||
if (states == null) {
|
|
||||||
bitSets[i] = states = FastBitSet.create(type.getMaxStateId() + 1);
|
|
||||||
}
|
|
||||||
FastBitSet.set(states, localI);
|
|
||||||
reset(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> BlockMaskBuilder add(BlockType type, Property<T> property, int index) {
|
|
||||||
AbstractProperty<T> prop = (AbstractProperty<T>) property;
|
|
||||||
long[] states = bitSets[type.getInternalId()];
|
|
||||||
if (states == ALL) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<T> values = property.getValues();
|
|
||||||
int localI = index << prop.getBitOffset() >> BlockTypesCache.BIT_OFFSET;
|
|
||||||
if (states == null || !FastBitSet.get(states, localI)) {
|
|
||||||
if (states == null) {
|
|
||||||
bitSets[type.getInternalId()] = states = FastBitSet.create(type.getMaxStateId() + 1);
|
|
||||||
}
|
|
||||||
set(type, states, property, index);
|
|
||||||
reset(false);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> BlockMaskBuilder filter(BlockType type, Property<T> property, int index) {
|
|
||||||
AbstractProperty<T> prop = (AbstractProperty<T>) property;
|
|
||||||
long[] states = bitSets[type.getInternalId()];
|
|
||||||
if (states == null) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
List<T> values = property.getValues();
|
|
||||||
int localI = index << prop.getBitOffset() >> BlockTypesCache.BIT_OFFSET;
|
|
||||||
if (states == ALL || FastBitSet.get(states, localI)) {
|
|
||||||
if (states == ALL) {
|
|
||||||
bitSets[type.getInternalId()] = states = FastBitSet.create(type.getMaxStateId() + 1);
|
|
||||||
FastBitSet.setAll(states);
|
|
||||||
}
|
|
||||||
clear(type, states, property, index);
|
|
||||||
reset(false);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applyRecursive(List<Property> properties, int propertiesIndex, int state, long[] states, boolean set) {
|
|
||||||
AbstractProperty current = (AbstractProperty) properties.get(propertiesIndex);
|
|
||||||
List values = current.getValues();
|
|
||||||
if (propertiesIndex + 1 < properties.size()) {
|
|
||||||
for (int i = 0; i < values.size(); i++) {
|
|
||||||
int newState = current.modifyIndex(state, i);
|
|
||||||
applyRecursive(properties, propertiesIndex + 1, newState, states, set);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < values.size(); i++) {
|
|
||||||
int index = current.modifyIndex(state, i) >> BlockTypesCache.BIT_OFFSET;
|
|
||||||
if (set) {
|
|
||||||
FastBitSet.set(states, index);
|
|
||||||
} else {
|
|
||||||
FastBitSet.clear(states, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void set(BlockType type, long[] bitSet, Property property, int index) {
|
|
||||||
FastBitSet.set(bitSet, index);
|
|
||||||
if (type.getProperties().size() > 1) {
|
|
||||||
ArrayList<Property> properties = new ArrayList<>(type.getProperties());
|
|
||||||
properties.remove(property);
|
|
||||||
int state = ((AbstractProperty) property).modifyIndex(type.getInternalId(), index);
|
|
||||||
applyRecursive(properties, 0, state, bitSet, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clear(BlockType type, long[] bitSet, Property property, int index) {
|
|
||||||
FastBitSet.clear(bitSet, index);
|
|
||||||
if (type.getProperties().size() > 1) {
|
|
||||||
ArrayList<Property> properties = new ArrayList<>(type.getProperties());
|
|
||||||
properties.remove(property);
|
|
||||||
int state = ((AbstractProperty) property).modifyIndex(type.getInternalId(), index);
|
|
||||||
applyRecursive(properties, 0, state, bitSet, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockMaskBuilder optimize() {
|
public BlockMaskBuilder optimize() {
|
||||||
if (!optimizedStates) {
|
if (!optimizedStates) {
|
||||||
for (int i = 0; i < bitSets.length; i++) {
|
for (int i = 0; i < bitSets.length; i++) {
|
||||||
@ -667,4 +543,56 @@ public class BlockMaskBuilder {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class FuzzyStateAllowingBuilder {
|
||||||
|
|
||||||
|
private final BlockType type;
|
||||||
|
private final Map<Property<?>, List<Integer>> masked = new HashMap<>();
|
||||||
|
|
||||||
|
private FuzzyStateAllowingBuilder(BlockType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockType getType() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Property<?>> getMaskedProperties() {
|
||||||
|
return masked
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> !e.getValue().isEmpty())
|
||||||
|
.map(Map.Entry::getKey)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void allow(Property<?> property, int index) {
|
||||||
|
checkNotNull(property);
|
||||||
|
if (!type.hasProperty(property.getKey())) {
|
||||||
|
throw new IllegalArgumentException(String.format(
|
||||||
|
"Property %s cannot be applied to block type %s",
|
||||||
|
property.getName(),
|
||||||
|
type.getId()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
masked.computeIfAbsent(property, k -> new ArrayList<>()).add(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean allows() {
|
||||||
|
//noinspection SimplifyStreamApiCallChains - Marginally faster like this
|
||||||
|
return !masked.isEmpty() && !masked.values().stream().anyMatch(List::isEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean allows(Property<?> property) {
|
||||||
|
return !masked.containsKey(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean allows(Property<?> property, int index) {
|
||||||
|
if (!masked.containsKey(property)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return masked.get(property).contains(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,10 @@ public class FastBitSet {
|
|||||||
Arrays.fill(bits, -1L);
|
Arrays.fill(bits, -1L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void unsetAll(long[] bits) {
|
||||||
|
Arrays.fill(bits, 0);
|
||||||
|
}
|
||||||
|
|
||||||
public static void and(long[] bits, final long[] other) {
|
public static void and(long[] bits, final long[] other) {
|
||||||
final int end = Math.min(other.length, bits.length);
|
final int end = Math.min(other.length, bits.length);
|
||||||
for (int i = 0; i < end; ++i) {
|
for (int i = 0; i < end; ++i) {
|
||||||
|
@ -182,7 +182,7 @@ public class FuzzyBlockState extends BlockState {
|
|||||||
checkNotNull(property);
|
checkNotNull(property);
|
||||||
checkNotNull(value);
|
checkNotNull(value);
|
||||||
checkNotNull(type, "The type must be set before the properties!");
|
checkNotNull(type, "The type must be set before the properties!");
|
||||||
type.getProperty(property.getName()); // Verify the property is valid for this type
|
checkNotNull(type.getProperty(property.getName())); // Verify the property is valid for this type
|
||||||
values.put(property, value);
|
values.put(property, value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren