mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-07-02 03:16:41 +00:00
Merge branch 'IntellectualSites:main' into main
This commit is contained in:
@ -356,8 +356,8 @@ public class Fawe {
|
||||
);
|
||||
if (Settings.settings().QUEUE.TARGET_SIZE < 4 * Settings.settings().QUEUE.PARALLEL_THREADS) {
|
||||
LOGGER.error(
|
||||
"queue.target_size is {}, and queue.parallel_threads is {}. It is HIGHLY recommended that queue" +
|
||||
".target_size be at least four times queue.parallel_threads or greater.",
|
||||
"queue.target-size is {}, and queue.parallel_threads is {}. It is HIGHLY recommended that queue" +
|
||||
".target-size be at least four times queue.parallel-threads or greater.",
|
||||
Settings.settings().QUEUE.TARGET_SIZE,
|
||||
Settings.settings().QUEUE.PARALLEL_THREADS
|
||||
);
|
||||
|
@ -584,7 +584,8 @@ public class Settings extends Config {
|
||||
@Comment({"Display constant titles about the progress of a user's edit",
|
||||
" - false = disabled",
|
||||
" - title = Display progress titles",
|
||||
" - chat = Display progress in chat"
|
||||
" - chat = Display progress in chat",
|
||||
" - Currently not implemented"
|
||||
})
|
||||
public String DISPLAY = "false";
|
||||
@Comment("How often edit progress is displayed")
|
||||
@ -622,10 +623,11 @@ public class Settings extends Config {
|
||||
public boolean PERSISTENT_BRUSHES = true;
|
||||
|
||||
@Comment({
|
||||
"[SAFE] Keep entities that are positioned in non-air blocks when editing an area",
|
||||
"Might cause client-side FPS lag in some situations"
|
||||
"[SAFE] Keep entities that are positioned in non-air blocks when editing an area (default: true)",
|
||||
" - Might cause client-side FPS lag in some situations",
|
||||
" - Requires fast-placement to be true"
|
||||
})
|
||||
public boolean KEEP_ENTITIES_IN_BLOCKS = false;
|
||||
public boolean KEEP_ENTITIES_IN_BLOCKS = true;
|
||||
|
||||
@Comment({
|
||||
"[SAFE] Attempt to remove entities from the world if they were not present in the expected chunk (default: true)",
|
||||
|
@ -0,0 +1,57 @@
|
||||
package com.fastasyncworldedit.core.extent.processor;
|
||||
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunk;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Processor that removes existing entities that would not be in air after the edit
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
public class EntityInBlockRemovingProcessor implements IBatchProcessor {
|
||||
|
||||
@Override
|
||||
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
for (CompoundTag tag : get.getEntities()) {
|
||||
// Empty tags for seemingly non-existent entities can exist?
|
||||
if (tag.getList("Pos").size() == 0) {
|
||||
continue;
|
||||
}
|
||||
BlockVector3 pos = tag.getEntityPosition().toBlockPoint();
|
||||
int x = pos.getX() & 15;
|
||||
int y = pos.getY();
|
||||
int z = pos.getZ() & 15;
|
||||
if (!set.hasSection(y >> 4)) {
|
||||
continue;
|
||||
}
|
||||
if (set.getBlock(x, y, z).getBlockType() != BlockTypes.__RESERVED__ && !set
|
||||
.getBlock(x, y, z)
|
||||
.getBlockType()
|
||||
.getMaterial()
|
||||
.isAir()) {
|
||||
set.removeEntity(tag.getUUID());
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Extent construct(final Extent child) {
|
||||
throw new UnsupportedOperationException("Processing only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessorScope getScope() {
|
||||
// After block removal but before history
|
||||
return ProcessorScope.CUSTOM;
|
||||
}
|
||||
|
||||
}
|
@ -7,7 +7,8 @@ package com.fastasyncworldedit.core.extent.processor;
|
||||
* - CHANGING_BLOCKS (processors that may ADD or CHANGE blocks being set)
|
||||
* - REMOVING_BLOCKS (processors that may ADD, CHANGE or REMOVE blocks being set)
|
||||
* - CUSTOM (processors that do not specify a SCOPE)
|
||||
* - READING_SET_BLOCKS (processors that do not alter blocks at all, and read the blocks that are actually going to set, e.g. history processors)
|
||||
* - READING_SET_BLOCKS (processors that do not alter blocks at all, and read the blocks that are actually going to set, e.g.
|
||||
* history processors). There is no guarantee that changes made here will be stored in history.
|
||||
*/
|
||||
public enum ProcessorScope {
|
||||
ADDING_BLOCKS(0),
|
||||
|
@ -20,12 +20,11 @@ import com.sk89q.worldedit.world.block.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -36,10 +35,12 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public class BlockMaskBuilder {
|
||||
|
||||
private static final Operator GREATER = (a, b) -> a > b;
|
||||
@ -63,58 +64,97 @@ public class BlockMaskBuilder {
|
||||
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);
|
||||
if (property == null) {
|
||||
return false;
|
||||
}
|
||||
List<Object> values = property.getValues();
|
||||
boolean result = false;
|
||||
List<Object> values = property.getValues();
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
Object value = values.get(i);
|
||||
if (!value.toString().matches(regex) && has(blockType, property, i)) {
|
||||
filter(blockType, property, i);
|
||||
if (values.get(i).toString().matches(regex)) {
|
||||
builder.allow(property, i);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
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);
|
||||
if (property == null) {
|
||||
return false;
|
||||
}
|
||||
int index = property.getIndexFor(value);
|
||||
int index = property.getIndexFor(stringValue);
|
||||
List<Object> values = property.getValues();
|
||||
boolean result = false;
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
if (!operator.test(index, i) && has(blockType, property, i)) {
|
||||
filter(blockType, property, i);
|
||||
if (operator.test(index, i)) {
|
||||
builder.allow(property, i);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean filterRegexOrOperator(BlockType type, PropertyKey key, Operator operator, CharSequence value) {
|
||||
boolean result = false;
|
||||
if (!type.hasProperty(key)) {
|
||||
if (operator == EQUAL) {
|
||||
result = bitSets[type.getInternalId()] != null;
|
||||
remove(type);
|
||||
private boolean handleRegexOrOperator(
|
||||
BlockType type,
|
||||
PropertyKey key,
|
||||
Operator operator,
|
||||
CharSequence value,
|
||||
FuzzyStateAllowingBuilder builder
|
||||
) {
|
||||
if (!type.hasProperty(key) && operator == EQUAL) {
|
||||
return false;
|
||||
}
|
||||
if (value.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
if ((operator == EQUAL || operator == EQUAL_OR_NULL) && !StringMan.isAlphanumericUnd(value)) {
|
||||
return handleRegex(type, key, value.toString(), builder);
|
||||
} else {
|
||||
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 {
|
||||
if (value.length() == 0) {
|
||||
return result;
|
||||
}
|
||||
if ((operator == EQUAL || operator == EQUAL_OR_NULL) && !StringMan.isAlphanumericUnd(value)) {
|
||||
result = filterRegex(type, key, value.toString());
|
||||
} else {
|
||||
result = filterOperator(type, key, operator, value);
|
||||
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 {
|
||||
@ -130,26 +170,28 @@ public class BlockMaskBuilder {
|
||||
charSequence.setString(input);
|
||||
charSequence.setSubstring(0, propStart);
|
||||
|
||||
BlockType type = null;
|
||||
List<BlockType> blockTypeList = null;
|
||||
List<BlockType> blockTypeList;
|
||||
List<FuzzyStateAllowingBuilder> builders;
|
||||
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);
|
||||
} else {
|
||||
String regex = charSequence.toString();
|
||||
blockTypeList = new ArrayList<>();
|
||||
for (BlockType myType : BlockTypesCache.values) {
|
||||
if (myType.getId().matches(regex)) {
|
||||
blockTypeList.add(myType);
|
||||
add(myType);
|
||||
builders = new ArrayList<>();
|
||||
Pattern pattern = Pattern.compile("(minecraft:)?" + regex);
|
||||
for (BlockType type : BlockTypesCache.values) {
|
||||
if (pattern.matcher(type.getId()).find()) {
|
||||
blockTypeList.add(type);
|
||||
builders.add(new FuzzyStateAllowingBuilder(type));
|
||||
add(type);
|
||||
}
|
||||
}
|
||||
if (blockTypeList.isEmpty()) {
|
||||
throw new InputParseException(Caption.of("fawe.error.no-block-found", TextComponent.of(input)));
|
||||
}
|
||||
if (blockTypeList.size() == 1) {
|
||||
type = blockTypeList.get(0);
|
||||
}
|
||||
}
|
||||
// Empty string
|
||||
charSequence.setSubstring(0, 0);
|
||||
@ -169,11 +211,11 @@ public class BlockMaskBuilder {
|
||||
}
|
||||
case ']', ',' -> {
|
||||
charSequence.setSubstring(last, i);
|
||||
if (key == null && PropertyKey.getByName(charSequence) == null) {
|
||||
if (key == null && (key = PropertyKey.getByName(charSequence)) == null) {
|
||||
suggest(
|
||||
input,
|
||||
charSequence.toString(),
|
||||
type != null ? Collections.singleton(type) : blockTypeList
|
||||
blockTypeList
|
||||
);
|
||||
}
|
||||
if (operator == null) {
|
||||
@ -182,34 +224,20 @@ public class BlockMaskBuilder {
|
||||
() -> Arrays.asList("=", "~", "!", "<", ">", "<=", ">=")
|
||||
);
|
||||
}
|
||||
boolean filtered = false;
|
||||
if (type != null) {
|
||||
filtered = filterRegexOrOperator(type, key, operator, charSequence);
|
||||
} else {
|
||||
for (BlockType myType : blockTypeList) {
|
||||
filtered |= filterRegexOrOperator(myType, key, operator, charSequence);
|
||||
for (int index = 0; index < blockTypeList.size(); index++) {
|
||||
if (!handleRegexOrOperator(
|
||||
blockTypeList.get(index),
|
||||
key,
|
||||
operator,
|
||||
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
|
||||
key = null;
|
||||
operator = null;
|
||||
@ -235,7 +263,7 @@ public class BlockMaskBuilder {
|
||||
suggest(
|
||||
input,
|
||||
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 {
|
||||
if (StringMan.isAlphanumericUnd(input)) {
|
||||
add(BlockTypes.parse(input));
|
||||
} else {
|
||||
boolean success = false;
|
||||
for (BlockType myType : BlockTypesCache.values) {
|
||||
if (myType.getId().matches(input)) {
|
||||
if (myType.getId().matches("(minecraft:)?" + input)) {
|
||||
add(myType);
|
||||
success = true;
|
||||
}
|
||||
@ -275,16 +308,6 @@ public class BlockMaskBuilder {
|
||||
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 {
|
||||
throw new SuggestInputParseException(Caption.of("worldedit.error.parser.unknown-property", property, input), () -> {
|
||||
Set<PropertyKey> keys = PropertyKeySet.empty();
|
||||
@ -381,42 +404,6 @@ public class BlockMaskBuilder {
|
||||
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) {
|
||||
bitSets[type.getInternalId()] = ALL;
|
||||
return this;
|
||||
@ -478,117 +465,6 @@ public class BlockMaskBuilder {
|
||||
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() {
|
||||
if (!optimizedStates) {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ public class SimpleChangeSetSummary implements ChangeSetSummary {
|
||||
public Map<BlockState, Integer> getBlocks() {
|
||||
HashMap<BlockState, Integer> map = new HashMap<>();
|
||||
for (int i = 0; i < blocks.length; i++) {
|
||||
if (blocks[i] != 0) {
|
||||
if (blocks[i] != BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
BlockState state = BlockTypesCache.states[i];
|
||||
map.put(state, blocks[i]);
|
||||
}
|
||||
|
@ -69,6 +69,10 @@ public class FastBitSet {
|
||||
Arrays.fill(bits, -1L);
|
||||
}
|
||||
|
||||
public static void unsetAll(long[] bits) {
|
||||
Arrays.fill(bits, 0);
|
||||
}
|
||||
|
||||
public static void and(long[] bits, final long[] other) {
|
||||
final int end = Math.min(other.length, bits.length);
|
||||
for (int i = 0; i < end; ++i) {
|
||||
|
@ -193,6 +193,7 @@ public class ParallelQueueExtent extends PassthroughExtent {
|
||||
public int setBlocks(Set<BlockVector3> vset, Pattern pattern) {
|
||||
if (vset instanceof Region) {
|
||||
this.changes = setBlocks((Region) vset, pattern);
|
||||
return this.changes;
|
||||
}
|
||||
// TODO optimize parallel
|
||||
for (BlockVector3 blockVector3 : vset) {
|
||||
|
@ -122,7 +122,7 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks {
|
||||
|
||||
@Override
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
return tiles.get(x, y, z);
|
||||
return tiles == null ? null : tiles.get(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -45,8 +45,8 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
|
||||
|
||||
private final ReentrantWrappedStampedLock calledLock = new ReentrantWrappedStampedLock();
|
||||
|
||||
private IChunkGet chunkExisting; // The existing chunk (e.g. a clipboard, or the world, before changes)
|
||||
private IChunkSet chunkSet; // The blocks to be set to the chunkExisting
|
||||
private volatile IChunkGet chunkExisting; // The existing chunk (e.g. a clipboard, or the world, before changes)
|
||||
private volatile IChunkSet chunkSet; // The blocks to be set to the chunkExisting
|
||||
private IBlockDelegate delegate; // delegate handles the abstraction of the chunk layers
|
||||
private IQueueExtent<? extends IChunk> extent; // the parent queue extent which has this chunk
|
||||
private int chunkX;
|
||||
@ -1042,13 +1042,12 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
|
||||
calledLock.lock();
|
||||
final long stamp = calledLock.getStampChecked();
|
||||
if (chunkSet != null && !chunkSet.isEmpty()) {
|
||||
this.delegate = GET;
|
||||
chunkSet.setBitMask(bitMask);
|
||||
try {
|
||||
return this.call(chunkSet.createCopy(), () -> {
|
||||
this.delegate = NULL;
|
||||
chunkSet = null;
|
||||
calledLock.unlock(stamp);
|
||||
});
|
||||
IChunkSet copy = chunkSet.createCopy();
|
||||
chunkSet = null;
|
||||
return this.call(copy, () -> calledLock.unlock(stamp));
|
||||
} catch (Throwable t) {
|
||||
calledLock.unlock();
|
||||
throw t;
|
||||
|
@ -17,10 +17,30 @@ public class FaweMask implements IDelegateRegion {
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the mask is still valid
|
||||
*
|
||||
* @param player player to test
|
||||
* @param type type of mask
|
||||
* @return if still valid
|
||||
*/
|
||||
public boolean isValid(Player player, FaweMaskManager.MaskType type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the mask is still valid
|
||||
*
|
||||
* @param player player to test
|
||||
* @param type type of mask
|
||||
* @param notify if the player should be notified
|
||||
* @return if still valid
|
||||
* @since TODO
|
||||
*/
|
||||
public boolean isValid(Player player, FaweMaskManager.MaskType type, boolean notify) {
|
||||
return isValid(player, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Region clone() {
|
||||
throw new UnsupportedOperationException("Clone not supported");
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.fastasyncworldedit.core.regions;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.regions.filter.RegionFilter;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
|
||||
import java.util.Locale;
|
||||
@ -28,6 +27,15 @@ public abstract class FaweMaskManager {
|
||||
*/
|
||||
public abstract FaweMask getMask(final Player player, MaskType type, boolean isWhitelist);
|
||||
|
||||
/**
|
||||
* Get a {@link FaweMask} for the given player and {@link MaskType}. If isWhitelist is false, will return a "blacklist" mask.
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
public FaweMask getMask(final Player player, MaskType type, boolean isWhitelist, boolean notify) {
|
||||
return getMask(player, type, isWhitelist);
|
||||
}
|
||||
|
||||
public boolean isExclusive() {
|
||||
return Settings.settings().REGION_RESTRICTIONS_OPTIONS.EXCLUSIVE_MANAGERS.contains(this.key);
|
||||
}
|
||||
|
@ -108,27 +108,22 @@ public class WEManager {
|
||||
}
|
||||
player.setMeta("lastMaskWorld", world);
|
||||
Set<FaweMask> masks = player.getMeta("lastMask");
|
||||
Set<Region> backupRegions = new HashSet<>();
|
||||
Set<Region> regions = new HashSet<>();
|
||||
|
||||
|
||||
if (masks == null || !isWhitelist) {
|
||||
masks = new HashSet<>();
|
||||
} else {
|
||||
synchronized (masks) {
|
||||
boolean removed = false;
|
||||
boolean inMask = false;
|
||||
if (!masks.isEmpty()) {
|
||||
Iterator<FaweMask> iterator = masks.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
FaweMask mask = iterator.next();
|
||||
if (mask.isValid(player, type)) {
|
||||
if (mask.isValid(player, type, false)) {
|
||||
Region region = mask.getRegion();
|
||||
if (region.contains(loc.toBlockPoint())) {
|
||||
regions.add(region);
|
||||
} else {
|
||||
removed = true;
|
||||
backupRegions.add(region);
|
||||
}
|
||||
inMask |= region.contains(loc.toBlockPoint());
|
||||
regions.add(region);
|
||||
} else {
|
||||
if (Settings.settings().ENABLED_COMPONENTS.DEBUG) {
|
||||
player.printDebug(Caption.of("fawe.error.region-mask-invalid", mask.getClass().getSimpleName()));
|
||||
@ -138,39 +133,44 @@ public class WEManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!removed) {
|
||||
if (!removed && inMask) {
|
||||
return regions.toArray(new Region[0]);
|
||||
}
|
||||
masks.clear();
|
||||
}
|
||||
}
|
||||
for (FaweMaskManager manager : managers) {
|
||||
if (player.hasPermission("fawe." + manager.getKey())) {
|
||||
try {
|
||||
if (manager.isExclusive() && !masks.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
final FaweMask mask = manager.getMask(player, FaweMaskManager.MaskType.getDefaultMaskType(), isWhitelist);
|
||||
if (mask != null) {
|
||||
regions.add(mask.getRegion());
|
||||
masks.add(mask);
|
||||
if (manager.isExclusive()) {
|
||||
break;
|
||||
synchronized (masks) {
|
||||
for (FaweMaskManager manager : managers) {
|
||||
if (player.hasPermission("fawe." + manager.getKey())) {
|
||||
try {
|
||||
if (manager.isExclusive() && !masks.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
final FaweMask mask = manager.getMask(
|
||||
player,
|
||||
FaweMaskManager.MaskType.getDefaultMaskType(),
|
||||
isWhitelist,
|
||||
masks.isEmpty()
|
||||
);
|
||||
if (mask != null) {
|
||||
regions.add(mask.getRegion());
|
||||
masks.add(mask);
|
||||
if (manager.isExclusive()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
} else {
|
||||
player.printError(TextComponent.of("Missing permission " + "fawe." + manager.getKey()));
|
||||
}
|
||||
} else {
|
||||
player.printError(TextComponent.of("Missing permission " + "fawe." + manager.getKey()));
|
||||
}
|
||||
}
|
||||
if (isWhitelist) {
|
||||
regions.addAll(backupRegions);
|
||||
if (!masks.isEmpty()) {
|
||||
player.setMeta("lastMask", masks);
|
||||
} else {
|
||||
player.deleteMeta("lastMask");
|
||||
if (isWhitelist) {
|
||||
if (!masks.isEmpty()) {
|
||||
player.setMeta("lastMask", masks);
|
||||
} else {
|
||||
player.deleteMeta("lastMask");
|
||||
}
|
||||
}
|
||||
}
|
||||
return regions.toArray(new Region[0]);
|
||||
|
@ -2161,10 +2161,12 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
final int ceilRadiusX = (int) Math.ceil(radiusX);
|
||||
final int ceilRadiusZ = (int) Math.ceil(radiusZ);
|
||||
|
||||
double xSqr;
|
||||
double zSqr;
|
||||
double distanceSq;
|
||||
double xSqr, zSqr, distanceSq;
|
||||
double xn, zn;
|
||||
double dx2, dz2;
|
||||
double nextXn = 0;
|
||||
double nextZn, nextMinZn;
|
||||
int xx, x_x, zz, z_z, yy;
|
||||
|
||||
if (thickness != 0) {
|
||||
double nextMinXn = 0;
|
||||
@ -2172,19 +2174,18 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
final double minInvRadiusZ = 1 / (radiusZ - thickness);
|
||||
forX:
|
||||
for (int x = 0; x <= ceilRadiusX; ++x) {
|
||||
final double xn = nextXn;
|
||||
double dx2 = nextMinXn * nextMinXn;
|
||||
xn = nextXn;
|
||||
dx2 = nextMinXn * nextMinXn;
|
||||
nextXn = (x + 1) * invRadiusX;
|
||||
nextMinXn = (x + 1) * minInvRadiusX;
|
||||
double nextZn = 0;
|
||||
double nextMinZn = 0;
|
||||
nextZn = 0;
|
||||
nextMinZn = 0;
|
||||
xSqr = xn * xn;
|
||||
xx = px + x;
|
||||
x_x = px - x;
|
||||
forZ:
|
||||
for (int z = 0; z <= ceilRadiusZ; ++z) {
|
||||
final double zn = nextZn;
|
||||
double dz2 = nextMinZn * nextMinZn;
|
||||
nextZn = (z + 1) * invRadiusZ;
|
||||
nextMinZn = (z + 1) * minInvRadiusZ;
|
||||
zn = nextZn;
|
||||
zSqr = zn * zn;
|
||||
distanceSq = xSqr + zSqr;
|
||||
if (distanceSq > 1) {
|
||||
@ -2193,16 +2194,23 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
}
|
||||
break forZ;
|
||||
}
|
||||
dz2 = nextMinZn * nextMinZn;
|
||||
nextZn = (z + 1) * invRadiusZ;
|
||||
nextMinZn = (z + 1) * minInvRadiusZ;
|
||||
|
||||
if ((dz2 + nextMinXn * nextMinXn <= 1) && (nextMinZn * nextMinZn + dx2 <= 1)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
zz = pz + z;
|
||||
z_z = pz - z;
|
||||
|
||||
for (int y = 0; y < height; ++y) {
|
||||
this.setBlock(mutableBlockVector3.setComponents(px + x, py + y, pz + z), block);
|
||||
this.setBlock(mutableBlockVector3.setComponents(px - x, py + y, pz + z), block);
|
||||
this.setBlock(mutableBlockVector3.setComponents(px + x, py + y, pz - z), block);
|
||||
this.setBlock(mutableBlockVector3.setComponents(px - x, py + y, pz - z), block);
|
||||
yy = py + y;
|
||||
this.setBlock(xx, yy, zz, block);
|
||||
this.setBlock(x_x, yy, zz, block);
|
||||
this.setBlock(xx, yy, z_z, block);
|
||||
this.setBlock(x_x, yy, z_z, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2210,14 +2218,17 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
//FAWE end
|
||||
forX:
|
||||
for (int x = 0; x <= ceilRadiusX; ++x) {
|
||||
final double xn = nextXn;
|
||||
xn = nextXn;
|
||||
nextXn = (x + 1) * invRadiusX;
|
||||
double nextZn = 0;
|
||||
nextZn = 0;
|
||||
xSqr = xn * xn;
|
||||
// FAWE start
|
||||
xx = px + x;
|
||||
x_x = px - x;
|
||||
//FAWE end
|
||||
forZ:
|
||||
for (int z = 0; z <= ceilRadiusZ; ++z) {
|
||||
final double zn = nextZn;
|
||||
nextZn = (z + 1) * invRadiusZ;
|
||||
zn = nextZn;
|
||||
zSqr = zn * zn;
|
||||
distanceSq = xSqr + zSqr;
|
||||
if (distanceSq > 1) {
|
||||
@ -2227,18 +2238,27 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
break forZ;
|
||||
}
|
||||
|
||||
// FAWE start
|
||||
nextZn = (z + 1) * invRadiusZ;
|
||||
//FAWE end
|
||||
if (!filled) {
|
||||
if ((zSqr + nextXn * nextXn <= 1) && (nextZn * nextZn + xSqr <= 1)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//FAWE start
|
||||
zz = pz + z;
|
||||
z_z = pz - z;
|
||||
//FAWE end
|
||||
|
||||
for (int y = 0; y < height; ++y) {
|
||||
//FAWE start - mutable
|
||||
this.setBlock(mutableBlockVector3.setComponents(px + x, py + y, pz + z), block);
|
||||
this.setBlock(mutableBlockVector3.setComponents(px - x, py + y, pz + z), block);
|
||||
this.setBlock(mutableBlockVector3.setComponents(px + x, py + y, pz - z), block);
|
||||
this.setBlock(mutableBlockVector3.setComponents(px - x, py + y, pz - z), block);
|
||||
//FAWE start
|
||||
yy = py + y;
|
||||
this.setBlock(xx, yy, zz, block);
|
||||
this.setBlock(x_x, yy, zz, block);
|
||||
this.setBlock(xx, yy, z_z, block);
|
||||
this.setBlock(x_x, yy, z_z, block);
|
||||
//FAWE end
|
||||
}
|
||||
}
|
||||
@ -2293,7 +2313,6 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
int px = pos.getBlockX();
|
||||
int py = pos.getBlockY();
|
||||
int pz = pos.getBlockZ();
|
||||
MutableBlockVector3 mutable = new MutableBlockVector3();
|
||||
|
||||
final int ceilRadiusX = (int) Math.ceil(radiusX);
|
||||
final int ceilRadiusY = (int) Math.ceil(radiusY);
|
||||
@ -2301,31 +2320,43 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
|
||||
double threshold = 0.5;
|
||||
|
||||
double dx, dy, dz, dxy, dxz, dyz, dxyz;
|
||||
int xx, x_x, yy, y_y, zz, z_z;
|
||||
double xnx, yny, znz;
|
||||
double nextXn = 0;
|
||||
double dx;
|
||||
double dy;
|
||||
double dz;
|
||||
double dxy;
|
||||
double dxyz;
|
||||
double nextYn, nextZn;
|
||||
double nextXnSq, nextYnSq, nextZnSq;
|
||||
double xn, yn, zn;
|
||||
forX:
|
||||
for (int x = 0; x <= ceilRadiusX; ++x) {
|
||||
final double xn = nextXn;
|
||||
xn = nextXn;
|
||||
dx = xn * xn;
|
||||
nextXn = (x + 1) * invRadiusX;
|
||||
double nextYn = 0;
|
||||
nextXnSq = nextXn * nextXn;
|
||||
nextYn = 0;
|
||||
xx = px + x;
|
||||
x_x = px - x;
|
||||
xnx = x * nx;
|
||||
forY:
|
||||
for (int y = 0; y <= ceilRadiusY; ++y) {
|
||||
final double yn = nextYn;
|
||||
yn = nextYn;
|
||||
dy = yn * yn;
|
||||
dxy = dx + dy;
|
||||
nextYn = (y + 1) * invRadiusY;
|
||||
double nextZn = 0;
|
||||
nextYnSq = nextYn * nextYn;
|
||||
nextZn = 0;
|
||||
yy = py + y;
|
||||
y_y = py - y;
|
||||
yny = y * ny;
|
||||
forZ:
|
||||
for (int z = 0; z <= ceilRadiusZ; ++z) {
|
||||
final double zn = nextZn;
|
||||
zn = nextZn;
|
||||
dz = zn * zn;
|
||||
dxyz = dxy + dz;
|
||||
dxz = dx + dz;
|
||||
dyz = dy + dz;
|
||||
nextZn = (z + 1) * invRadiusZ;
|
||||
nextZnSq = nextZn * nextZn;
|
||||
if (dxyz > 1) {
|
||||
if (z == 0) {
|
||||
if (y == 0) {
|
||||
@ -2336,34 +2367,37 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
break forZ;
|
||||
}
|
||||
if (!filled) {
|
||||
if (nextXn * nextXn + dy + dz <= 1 && nextYn * nextYn + dx + dz <= 1 && nextZn * nextZn + dx + dy <= 1) {
|
||||
if (nextXnSq + dyz <= 1 && nextYnSq + dxz <= 1 && nextZnSq + dxy <= 1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
zz = pz + z;
|
||||
z_z = pz - z;
|
||||
znz = z * nz;
|
||||
|
||||
if (Math.abs((x) * nx + (y) * ny + (z) * nz) < threshold) {
|
||||
setBlock(mutable.setComponents(px + x, py + y, pz + z), block);
|
||||
if (Math.abs(xnx + yny + znz) < threshold) {
|
||||
setBlock(xx, yy, zz, block);
|
||||
}
|
||||
if (Math.abs((-x) * nx + (y) * ny + (z) * nz) < threshold) {
|
||||
setBlock(mutable.setComponents(px - x, py + y, pz + z), block);
|
||||
if (Math.abs(-xnx + yny + znz) < threshold) {
|
||||
setBlock(x_x, yy, zz, block);
|
||||
}
|
||||
if (Math.abs((x) * nx + (-y) * ny + (z) * nz) < threshold) {
|
||||
setBlock(mutable.setComponents(px + x, py - y, pz + z), block);
|
||||
if (Math.abs(xnx - yny + znz) < threshold) {
|
||||
setBlock(xx, y_y, zz, block);
|
||||
}
|
||||
if (Math.abs((x) * nx + (y) * ny + (-z) * nz) < threshold) {
|
||||
setBlock(mutable.setComponents(px + x, py + y, pz - z), block);
|
||||
if (Math.abs(xnx + yny - znz) < threshold) {
|
||||
setBlock(xx, yy, z_z, block);
|
||||
}
|
||||
if (Math.abs((-x) * nx + (-y) * ny + (z) * nz) < threshold) {
|
||||
setBlock(mutable.setComponents(px - x, py - y, pz + z), block);
|
||||
if (Math.abs(-xnx - yny + znz) < threshold) {
|
||||
setBlock(x_x, y_y, zz, block);
|
||||
}
|
||||
if (Math.abs((x) * nx + (-y) * ny + (-z) * nz) < threshold) {
|
||||
setBlock(mutable.setComponents(px + x, py - y, pz - z), block);
|
||||
if (Math.abs(xnx - yny - znz) < threshold) {
|
||||
setBlock(xx, y_y, z_z, block);
|
||||
}
|
||||
if (Math.abs((-x) * nx + (y) * ny + (-z) * nz) < threshold) {
|
||||
setBlock(mutable.setComponents(px - x, py + y, pz - z), block);
|
||||
if (Math.abs(-xnx + yny - znz) < threshold) {
|
||||
setBlock(x_x, yy, z_z, block);
|
||||
}
|
||||
if (Math.abs((-x) * nx + (-y) * ny + (-z) * nz) < threshold) {
|
||||
setBlock(mutable.setComponents(px - x, py - y, pz - z), block);
|
||||
if (Math.abs(-xnx - yny - znz) < threshold) {
|
||||
setBlock(x_x, y_y, z_z, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2418,29 +2452,38 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
final int ceilRadiusZ = (int) Math.ceil(radiusZ);
|
||||
|
||||
//FAWE start
|
||||
int yy;
|
||||
//FAWE end
|
||||
|
||||
double nextXn = 0;
|
||||
double nextYn, nextZn;
|
||||
double nextXnSq, nextYnSq, nextZnSq;
|
||||
double xn, yn, zn, dx, dy, dz;
|
||||
double dxy, dxz, dyz, dxyz;
|
||||
int xx, x_x, yy, zz, z_z;
|
||||
|
||||
forX:
|
||||
for (int x = 0; x <= ceilRadiusX; ++x) {
|
||||
final double xn = nextXn;
|
||||
double dx = xn * xn;
|
||||
xn = nextXn;
|
||||
dx = xn * xn;
|
||||
nextXn = (x + 1) * invRadiusX;
|
||||
double nextZn = 0;
|
||||
nextXnSq = nextXn * nextXn;
|
||||
xx = px + x;
|
||||
x_x = px - x;
|
||||
nextZn = 0;
|
||||
forZ:
|
||||
for (int z = 0; z <= ceilRadiusZ; ++z) {
|
||||
final double zn = nextZn;
|
||||
double dz = zn * zn;
|
||||
double dxz = dx + dz;
|
||||
zn = nextZn;
|
||||
dz = zn * zn;
|
||||
dxz = dx + dz;
|
||||
nextZn = (z + 1) * invRadiusZ;
|
||||
double nextYn = 0;
|
||||
nextZnSq = nextZn * nextZn;
|
||||
zz = pz + z;
|
||||
z_z = pz - z;
|
||||
nextYn = 0;
|
||||
|
||||
forY:
|
||||
for (int y = 0; y <= ceilRadiusY; ++y) {
|
||||
final double yn = nextYn;
|
||||
double dy = yn * yn;
|
||||
double dxyz = dxz + dy;
|
||||
yn = nextYn;
|
||||
dy = yn * yn;
|
||||
dxyz = dxz + dy;
|
||||
nextYn = (y + 1) * invRadiusY;
|
||||
|
||||
if (dxyz > 1) {
|
||||
@ -2453,40 +2496,45 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
break forY;
|
||||
}
|
||||
|
||||
nextYnSq = nextYn * nextYn;
|
||||
dxy = dx + dy;
|
||||
dyz = dy + dz;
|
||||
|
||||
if (!filled) {
|
||||
if (nextXn * nextXn + dy + dz <= 1 && nextYn * nextYn + dx + dz <= 1 && nextZn * nextZn + dx + dy <= 1) {
|
||||
if (nextXnSq + dyz <= 1 && nextYnSq + dxz <= 1 && nextZnSq + dxy <= 1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//FAWE start
|
||||
yy = py + y;
|
||||
if (yy <= maxY) {
|
||||
this.setBlock(px + x, py + y, pz + z, block);
|
||||
this.setBlock(xx, yy, zz, block);
|
||||
if (x != 0) {
|
||||
this.setBlock(px - x, py + y, pz + z, block);
|
||||
this.setBlock(x_x, yy, zz, block);
|
||||
}
|
||||
if (z != 0) {
|
||||
this.setBlock(px + x, py + y, pz - z, block);
|
||||
this.setBlock(xx, yy, z_z, block);
|
||||
if (x != 0) {
|
||||
this.setBlock(px - x, py + y, pz - z, block);
|
||||
this.setBlock(x_x, yy, z_z, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (y != 0 && (yy = py - y) >= minY) {
|
||||
this.setBlock(px + x, yy, pz + z, block);
|
||||
this.setBlock(xx, yy, zz, block);
|
||||
if (x != 0) {
|
||||
this.setBlock(px - x, yy, pz + z, block);
|
||||
this.setBlock(x_x, yy, zz, block);
|
||||
}
|
||||
if (z != 0) {
|
||||
this.setBlock(px + x, yy, pz - z, block);
|
||||
this.setBlock(xx, yy, z_z, block);
|
||||
if (x != 0) {
|
||||
this.setBlock(px - x, yy, pz - z, block);
|
||||
this.setBlock(x_x, yy, z_z, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//FAWE end
|
||||
|
||||
return changes;
|
||||
//FAWE end
|
||||
@ -2509,17 +2557,22 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
int bz = position.getZ();
|
||||
|
||||
int height = size;
|
||||
int yy, xx, x_x, zz, z_z;
|
||||
|
||||
for (int y = 0; y <= height; ++y) {
|
||||
size--;
|
||||
yy = y + by;
|
||||
for (int x = 0; x <= size; ++x) {
|
||||
xx = bx + x;
|
||||
x_x = bx - x;
|
||||
for (int z = 0; z <= size; ++z) {
|
||||
|
||||
zz = bz + z;
|
||||
z_z = bz - z;
|
||||
if ((filled && z <= size && x <= size) || z == size || x == size) {
|
||||
setBlock(x + bx, y + by, z + bz, block);
|
||||
setBlock(-x + bx, y + by, z + bz, block);
|
||||
setBlock(x + bx, y + by, -z + bz, block);
|
||||
setBlock(-x + bx, y + by, -z + bz, block);
|
||||
setBlock(xx, yy, zz, block);
|
||||
setBlock(x_x, yy, zz, block);
|
||||
setBlock(xx, yy, z_z, block);
|
||||
setBlock(x_x, yy, z_z, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3774,19 +3827,28 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
int radiusSqr = (int) (size * size);
|
||||
int sizeInt = (int) size * 2;
|
||||
|
||||
int xx, yy, zz;
|
||||
double distance;
|
||||
double noise;
|
||||
|
||||
if (sphericity == 1) {
|
||||
double nx, ny, nz;
|
||||
double d1, d2;
|
||||
for (int x = -sizeInt; x <= sizeInt; x++) {
|
||||
double nx = seedX + x * distort;
|
||||
double d1 = x * x * modX;
|
||||
nx = seedX + x * distort;
|
||||
d1 = x * x * modX;
|
||||
xx = px + x;
|
||||
for (int y = -sizeInt; y <= sizeInt; y++) {
|
||||
double d2 = d1 + y * y * modY;
|
||||
double ny = seedY + y * distort;
|
||||
d2 = d1 + y * y * modY;
|
||||
ny = seedY + y * distort;
|
||||
yy = py + y;
|
||||
for (int z = -sizeInt; z <= sizeInt; z++) {
|
||||
double nz = seedZ + z * distort;
|
||||
double distance = d2 + z * z * modZ;
|
||||
double noise = amplitude * SimplexNoise.noise(nx, ny, nz);
|
||||
nz = seedZ + z * distort;
|
||||
distance = d2 + z * z * modZ;
|
||||
zz = pz + z;
|
||||
noise = amplitude * SimplexNoise.noise(nx, ny, nz);
|
||||
if (distance + distance * noise < radiusSqr) {
|
||||
setBlock(px + x, py + y, pz + z, pattern);
|
||||
setBlock(xx, yy, zz, pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3803,39 +3865,48 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
|
||||
MutableVector3 mutable = new MutableVector3();
|
||||
double roughness = 1 - sphericity;
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
double xScaled;
|
||||
double yScaled;
|
||||
double zScaled;
|
||||
double manDist;
|
||||
double distSqr;
|
||||
for (int xr = -sizeInt; xr <= sizeInt; xr++) {
|
||||
xx = px + xr;
|
||||
for (int yr = -sizeInt; yr <= sizeInt; yr++) {
|
||||
yy = py + yr;
|
||||
for (int zr = -sizeInt; zr <= sizeInt; zr++) {
|
||||
zz = pz + zr;
|
||||
// pt == mutable as it's a MutableVector3
|
||||
// so it must be set each time
|
||||
mutable.mutX(xr);
|
||||
mutable.mutY(yr);
|
||||
mutable.mutZ(zr);
|
||||
mutable.setComponents(xr, yr, zr);
|
||||
Vector3 pt = transform.apply(mutable);
|
||||
int x = MathMan.roundInt(pt.getX());
|
||||
int y = MathMan.roundInt(pt.getY());
|
||||
int z = MathMan.roundInt(pt.getZ());
|
||||
x = MathMan.roundInt(pt.getX());
|
||||
y = MathMan.roundInt(pt.getY());
|
||||
z = MathMan.roundInt(pt.getZ());
|
||||
|
||||
double xScaled = Math.abs(x) * modX;
|
||||
double yScaled = Math.abs(y) * modY;
|
||||
double zScaled = Math.abs(z) * modZ;
|
||||
double manDist = xScaled + yScaled + zScaled;
|
||||
double distSqr = x * x * modX + z * z * modZ + y * y * modY;
|
||||
xScaled = Math.abs(x) * modX;
|
||||
yScaled = Math.abs(y) * modY;
|
||||
zScaled = Math.abs(z) * modZ;
|
||||
manDist = xScaled + yScaled + zScaled;
|
||||
distSqr = x * x * modX + z * z * modZ + y * y * modY;
|
||||
|
||||
double distance = Math.sqrt(distSqr) * sphericity + MathMan.max(
|
||||
distance = Math.sqrt(distSqr) * sphericity + MathMan.max(
|
||||
manDist,
|
||||
xScaled * manScaleX,
|
||||
yScaled * manScaleY,
|
||||
zScaled * manScaleZ
|
||||
) * roughness;
|
||||
|
||||
double noise = amplitude * SimplexNoise.noise(
|
||||
noise = amplitude * SimplexNoise.noise(
|
||||
seedX + x * distort,
|
||||
seedZ + z * distort,
|
||||
seedZ + z * distort
|
||||
);
|
||||
if (distance + distance * noise < r) {
|
||||
setBlock(px + xr, py + yr, pz + zr, pattern);
|
||||
setBlock(xx, yy, zz, pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import com.fastasyncworldedit.core.extent.NullExtent;
|
||||
import com.fastasyncworldedit.core.extent.SingleRegionExtent;
|
||||
import com.fastasyncworldedit.core.extent.SlowExtent;
|
||||
import com.fastasyncworldedit.core.extent.StripNBTExtent;
|
||||
import com.fastasyncworldedit.core.extent.processor.EntityInBlockRemovingProcessor;
|
||||
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightmapProcessor;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
||||
@ -72,7 +73,6 @@ import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
@ -99,7 +99,6 @@ public final class EditSessionBuilder {
|
||||
private RelightMode relightMode;
|
||||
private Relighter relighter;
|
||||
private Boolean wnaMode;
|
||||
private AbstractChangeSet changeTask;
|
||||
private Extent bypassHistory;
|
||||
private Extent bypassAll;
|
||||
private Extent extent;
|
||||
@ -522,7 +521,6 @@ public final class EditSessionBuilder {
|
||||
changeSet = new BlockBagChangeSet(changeSet, blockBag, limit.INVENTORY_MODE == 1);
|
||||
}
|
||||
if (combineStages) {
|
||||
changeTask = changeSet;
|
||||
this.extent = extent.enableHistory(changeSet);
|
||||
} else {
|
||||
this.extent = new HistoryExtent(extent, changeSet);
|
||||
@ -546,25 +544,6 @@ public final class EditSessionBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
FaweRegionExtent regionExtent = null;
|
||||
if (disallowedRegions != null) { // Always use MultiRegionExtent if we have blacklist regions
|
||||
regionExtent = new MultiRegionExtent(this.extent, this.limit, allowedRegions, disallowedRegions);
|
||||
} else if (allowedRegions == null) {
|
||||
allowedRegions = new Region[]{RegionWrapper.GLOBAL()};
|
||||
} else {
|
||||
if (allowedRegions.length == 0) {
|
||||
regionExtent = new NullExtent(this.extent, FaweCache.NO_REGION);
|
||||
} else {
|
||||
if (allowedRegions.length == 1) {
|
||||
regionExtent = new SingleRegionExtent(this.extent, this.limit, allowedRegions[0]);
|
||||
} else {
|
||||
regionExtent = new MultiRegionExtent(this.extent, this.limit, allowedRegions, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (placeChunks && regionExtent != null) {
|
||||
queue.addProcessor(regionExtent);
|
||||
}
|
||||
// There's no need to do the below (and it'll also just be a pain to implement) if we're not placing chunks
|
||||
if (placeChunks) {
|
||||
if (((relightMode != null && relightMode != RelightMode.NONE) || (relightMode == null && Settings.settings().LIGHTING.MODE > 0))) {
|
||||
@ -574,6 +553,11 @@ public final class EditSessionBuilder {
|
||||
queue.addProcessor(new RelightProcessor(relighter));
|
||||
}
|
||||
queue.addProcessor(new HeightmapProcessor(world.getMinY(), world.getMaxY()));
|
||||
|
||||
if (!Settings.settings().EXPERIMENTAL.KEEP_ENTITIES_IN_BLOCKS) {
|
||||
queue.addProcessor(new EntityInBlockRemovingProcessor());
|
||||
}
|
||||
|
||||
IBatchProcessor platformProcessor = WorldEdit
|
||||
.getInstance()
|
||||
.getPlatformManager()
|
||||
@ -593,24 +577,13 @@ public final class EditSessionBuilder {
|
||||
} else {
|
||||
relighter = NullRelighter.INSTANCE;
|
||||
}
|
||||
Consumer<Component> onErrorMessage;
|
||||
if (getActor() != null) {
|
||||
onErrorMessage = c -> getActor().print(Caption.of("fawe.error.occurred-continuing", c));
|
||||
} else {
|
||||
onErrorMessage = c -> {
|
||||
};
|
||||
}
|
||||
if (limit != null && !limit.isUnlimited() && regionExtent != null) {
|
||||
this.extent = new LimitExtent(regionExtent, limit, onErrorMessage);
|
||||
} else if (limit != null && !limit.isUnlimited()) {
|
||||
this.extent = new LimitExtent(this.extent, limit, onErrorMessage);
|
||||
} else if (regionExtent != null) {
|
||||
this.extent = regionExtent;
|
||||
}
|
||||
if (this.limit != null && this.limit.STRIP_NBT != null && !this.limit.STRIP_NBT.isEmpty()) {
|
||||
this.extent = new StripNBTExtent(this.extent, this.limit.STRIP_NBT);
|
||||
StripNBTExtent ext = new StripNBTExtent(this.extent, this.limit.STRIP_NBT);
|
||||
if (placeChunks) {
|
||||
queue.addProcessor((IBatchProcessor) this.extent);
|
||||
queue.addProcessor(ext);
|
||||
}
|
||||
if (!placeChunks || !combineStages) {
|
||||
this.extent = ext;
|
||||
}
|
||||
}
|
||||
if (this.limit != null && !this.limit.isUnlimited()) {
|
||||
@ -623,12 +596,50 @@ public final class EditSessionBuilder {
|
||||
}
|
||||
Set<PropertyRemap<?>> remaps = this.limit.REMAP_PROPERTIES;
|
||||
if (!limitBlocks.isEmpty() || (remaps != null && !remaps.isEmpty())) {
|
||||
this.extent = new DisallowedBlocksExtent(this.extent, limitBlocks, remaps);
|
||||
DisallowedBlocksExtent ext = new DisallowedBlocksExtent(this.extent, limitBlocks, remaps);
|
||||
if (placeChunks) {
|
||||
queue.addProcessor((IBatchProcessor) this.extent);
|
||||
queue.addProcessor(ext);
|
||||
}
|
||||
if (!placeChunks || !combineStages) {
|
||||
this.extent = ext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FaweRegionExtent regionExtent = null;
|
||||
if (disallowedRegions != null) { // Always use MultiRegionExtent if we have blacklist regions
|
||||
regionExtent = new MultiRegionExtent(this.extent, this.limit, allowedRegions, disallowedRegions);
|
||||
} else if (allowedRegions == null) {
|
||||
allowedRegions = new Region[]{RegionWrapper.GLOBAL()};
|
||||
} else {
|
||||
if (allowedRegions.length == 0) {
|
||||
regionExtent = new NullExtent(this.extent, FaweCache.NO_REGION);
|
||||
} else {
|
||||
if (allowedRegions.length == 1) {
|
||||
regionExtent = new SingleRegionExtent(this.extent, this.limit, allowedRegions[0]);
|
||||
} else {
|
||||
regionExtent = new MultiRegionExtent(this.extent, this.limit, allowedRegions, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (regionExtent != null) {
|
||||
if (placeChunks) {
|
||||
queue.addProcessor(regionExtent);
|
||||
}
|
||||
if (!placeChunks || !combineStages) {
|
||||
this.extent = regionExtent;
|
||||
}
|
||||
}
|
||||
Consumer<Component> onErrorMessage;
|
||||
if (getActor() != null) {
|
||||
onErrorMessage = c -> getActor().print(Caption.of("fawe.error.occurred-continuing", c));
|
||||
} else {
|
||||
onErrorMessage = c -> {
|
||||
};
|
||||
}
|
||||
if (limit != null && !limit.isUnlimited()) {
|
||||
this.extent = new LimitExtent(this.extent, limit, onErrorMessage);
|
||||
}
|
||||
this.extent = wrapExtent(this.extent, eventBus, event, EditSession.Stage.BEFORE_HISTORY);
|
||||
}
|
||||
return this;
|
||||
@ -699,7 +710,7 @@ public final class EditSessionBuilder {
|
||||
* Get the change set that will be used for history
|
||||
*/
|
||||
public AbstractChangeSet getChangeTask() {
|
||||
return changeTask;
|
||||
return changeSet;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,17 +160,7 @@ public class ExtentEntityCopy implements EntityFunction {
|
||||
// Remove
|
||||
if (isRemoving() && success) {
|
||||
//FAWE start
|
||||
UUID uuid = null;
|
||||
if (tag.containsKey("UUID")) {
|
||||
int[] arr = tag.getIntArray("UUID");
|
||||
uuid = new UUID((long) arr[0] << 32 | (arr[1] & 0xFFFFFFFFL), (long) arr[2] << 32 | (arr[3] & 0xFFFFFFFFL));
|
||||
} else if (tag.containsKey("UUIDMost")) {
|
||||
uuid = new UUID(tag.getLong("UUIDMost"), tag.getLong("UUIDLeast"));
|
||||
} else if (tag.containsKey("WorldUUIDMost")) {
|
||||
uuid = new UUID(tag.getLong("WorldUUIDMost"), tag.getLong("WorldUUIDLeast"));
|
||||
} else if (tag.containsKey("PersistentIDMSB")) {
|
||||
uuid = new UUID(tag.getLong("PersistentIDMSB"), tag.getLong("PersistentIDLSB"));
|
||||
}
|
||||
UUID uuid = entity.getState().getNbtData().getUUID();
|
||||
if (uuid != null) {
|
||||
if (source != null) {
|
||||
source.removeEntity(
|
||||
|
@ -229,6 +229,7 @@ public abstract class BreadthFirstSearch implements Operation {
|
||||
*/
|
||||
public void visit(BlockVector3 position) {
|
||||
if (!visited.contains(position)) {
|
||||
isVisitable(position, position); // Ignore this, just to initialize mask on this point
|
||||
queue.add(position);
|
||||
visited.add(position);
|
||||
}
|
||||
|
@ -165,6 +165,9 @@ public final class TreeGenerator {
|
||||
}
|
||||
//FAWE end
|
||||
},
|
||||
MANGROVE("Mangrove tree", "mangrove"),
|
||||
TALL_MANGROVE("Tall mangrove tree", "tall_mangrove"),
|
||||
CHERRY("Cherry blossom", "cherry"),
|
||||
RANDOM("Random tree", "rand", "random") {
|
||||
@Override
|
||||
public boolean generate(EditSession editSession, BlockVector3 pos) throws MaxChangedBlocksException {
|
||||
|
@ -78,13 +78,35 @@ public class BlockType implements Keyed, Pattern {
|
||||
this.id = i == -1 ? id : id.substring(0, i);
|
||||
this.settings = new BlockTypesCache.Settings(this, id, internalId, states);
|
||||
}
|
||||
//FAWE end
|
||||
|
||||
//FAWE start
|
||||
/**
|
||||
* @deprecated You should not be initialising your own BlockTypes, use {@link BlockTypes#get(String)} instead. If there is
|
||||
* a specific requirement to actually create new block types, please contact the FAWE devs to discuss. Use
|
||||
* {@link BlockTypes#get(String)} instead.
|
||||
*/
|
||||
@Deprecated(since = "TODO")
|
||||
//FAWE end
|
||||
public BlockType(String id) {
|
||||
this(id, null);
|
||||
}
|
||||
|
||||
//FAWE start
|
||||
/**
|
||||
* @deprecated You should not be initialising your own BlockTypes, use {@link BlockTypes#get(String)} instead. If there is
|
||||
* a specific requirement to actually create new block types, please contact the FAWE devs to discuss. Use
|
||||
* {@link BlockTypes#get(String)} instead.
|
||||
*/
|
||||
@Deprecated(since = "TODO")
|
||||
//FAWE end
|
||||
public BlockType(String id, Function<BlockState, BlockState> values) {
|
||||
// If it has no namespace, assume minecraft.
|
||||
if (!id.contains(":")) {
|
||||
id = "minecraft:" + id;
|
||||
}
|
||||
this.id = id;
|
||||
//FAWE start
|
||||
//TODO fix the line below
|
||||
this.settings = new BlockTypesCache.Settings(this, id, 0, null);
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ public class FuzzyBlockState extends BlockState {
|
||||
checkNotNull(property);
|
||||
checkNotNull(value);
|
||||
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);
|
||||
return this;
|
||||
}
|
||||
|
Reference in New Issue
Block a user