2018-06-17 05:42:47 +00:00
|
|
|
/*
|
|
|
|
* WorldEdit, a Minecraft world manipulation toolkit
|
|
|
|
* Copyright (C) sk89q <http://www.sk89q.com>
|
|
|
|
* Copyright (C) WorldEdit team and contributors
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU Lesser General Public License as published by the
|
|
|
|
* Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
|
|
* for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2018-07-05 08:15:51 +00:00
|
|
|
package com.sk89q.worldedit.world.block;
|
2018-06-17 05:42:47 +00:00
|
|
|
|
2018-08-21 17:22:37 +00:00
|
|
|
import com.boydti.fawe.command.SuggestInputParseException;
|
2018-08-12 14:03:07 +00:00
|
|
|
import com.boydti.fawe.object.string.MutableCharSequence;
|
2019-04-11 11:32:32 +00:00
|
|
|
import com.boydti.fawe.util.StringMan;
|
2018-08-12 14:03:07 +00:00
|
|
|
import com.google.common.base.Function;
|
2018-06-17 05:42:47 +00:00
|
|
|
import com.google.common.collect.Maps;
|
2018-08-10 10:29:06 +00:00
|
|
|
import com.sk89q.jnbt.CompoundTag;
|
2018-07-18 07:39:25 +00:00
|
|
|
import com.sk89q.worldedit.WorldEdit;
|
2018-08-12 14:03:07 +00:00
|
|
|
import com.sk89q.worldedit.WorldEditException;
|
2018-08-21 17:22:37 +00:00
|
|
|
import com.sk89q.worldedit.extension.input.InputParseException;
|
2019-04-03 13:25:16 +00:00
|
|
|
import com.sk89q.worldedit.extension.platform.Capability;
|
2018-08-12 14:03:07 +00:00
|
|
|
import com.sk89q.worldedit.extent.Extent;
|
2019-04-03 11:28:57 +00:00
|
|
|
import com.sk89q.worldedit.function.pattern.FawePattern;
|
2019-01-09 07:13:44 +00:00
|
|
|
import com.sk89q.worldedit.math.BlockVector3;
|
2018-08-12 14:03:07 +00:00
|
|
|
import com.sk89q.worldedit.registry.state.AbstractProperty;
|
2018-07-05 23:16:52 +00:00
|
|
|
import com.sk89q.worldedit.registry.state.Property;
|
2018-08-12 14:03:07 +00:00
|
|
|
import com.sk89q.worldedit.registry.state.PropertyKey;
|
2019-01-31 15:08:58 +00:00
|
|
|
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
2018-06-17 05:42:47 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
import javax.annotation.Nullable;
|
2019-06-04 15:48:30 +00:00
|
|
|
import java.util.*;
|
2019-04-04 14:24:47 +00:00
|
|
|
import java.util.stream.Collectors;
|
|
|
|
import java.util.stream.Stream;
|
2018-06-17 05:42:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* An immutable class that represents the state a block can be in.
|
|
|
|
*/
|
2018-06-17 13:13:22 +00:00
|
|
|
@SuppressWarnings("unchecked")
|
2019-04-03 11:28:57 +00:00
|
|
|
public class BlockState implements BlockStateHolder<BlockState>, FawePattern {
|
2019-04-04 14:24:47 +00:00
|
|
|
private final int internalId;
|
|
|
|
private final int ordinal;
|
|
|
|
private final BlockType blockType;
|
2019-04-03 13:25:16 +00:00
|
|
|
private BlockMaterial material;
|
2019-01-31 15:08:58 +00:00
|
|
|
private BaseBlock emptyBaseBlock;
|
2019-04-04 14:24:47 +00:00
|
|
|
|
|
|
|
protected BlockState(BlockType blockType, int internalId, int ordinal) {
|
2019-01-31 15:08:58 +00:00
|
|
|
this.blockType = blockType;
|
2019-04-03 13:25:16 +00:00
|
|
|
this.internalId = internalId;
|
|
|
|
this.ordinal = ordinal;
|
2019-04-04 14:24:47 +00:00
|
|
|
this.emptyBaseBlock = new BaseBlock(this);
|
2019-01-31 15:08:58 +00:00
|
|
|
}
|
2019-04-03 13:25:16 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
/**
|
|
|
|
* Returns a temporary BlockState for a given internal id
|
|
|
|
* @param combinedId
|
|
|
|
* @deprecated magic number
|
|
|
|
* @return BlockState
|
|
|
|
*/
|
2019-07-06 00:46:48 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
@Deprecated
|
2018-08-27 16:56:28 +00:00
|
|
|
public static BlockState getFromInternalId(int combinedId) throws InputParseException {
|
2018-08-12 14:03:07 +00:00
|
|
|
return BlockTypes.getFromStateId(combinedId).withStateId(combinedId);
|
|
|
|
}
|
|
|
|
|
2018-08-27 16:56:28 +00:00
|
|
|
@Deprecated
|
|
|
|
public static BlockState getFromOrdinal(int ordinal) {
|
|
|
|
return BlockTypes.states[ordinal];
|
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
/**
|
|
|
|
* Returns a temporary BlockState for a given type and string
|
|
|
|
* @param state String e.g. minecraft:water[level=4]
|
|
|
|
* @return BlockState
|
|
|
|
*/
|
2018-08-21 17:22:37 +00:00
|
|
|
public static BlockState get(String state) throws InputParseException {
|
2018-08-12 14:03:07 +00:00
|
|
|
return get(null, state);
|
|
|
|
}
|
2018-06-17 05:42:47 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
/**
|
|
|
|
* Returns a temporary BlockState for a given type and string
|
|
|
|
* - It's faster if a BlockType is provided compared to parsing the string
|
|
|
|
* @param type BlockType e.g. BlockTypes.STONE (or null)
|
|
|
|
* @param state String e.g. minecraft:water[level=4]
|
|
|
|
* @return BlockState
|
|
|
|
*/
|
2018-08-21 17:22:37 +00:00
|
|
|
public static BlockState get(@Nullable BlockType type, String state) throws InputParseException {
|
2018-08-27 16:56:28 +00:00
|
|
|
return get(type, state, null);
|
2018-06-17 05:42:47 +00:00
|
|
|
}
|
|
|
|
|
2018-06-17 13:13:22 +00:00
|
|
|
/**
|
2018-08-12 14:03:07 +00:00
|
|
|
* Returns a temporary BlockState for a given type and string
|
|
|
|
* - It's faster if a BlockType is provided compared to parsing the string
|
|
|
|
* @param type BlockType e.g. BlockTypes.STONE (or null)
|
|
|
|
* @param state String e.g. minecraft:water[level=4]
|
|
|
|
* @return BlockState
|
2018-06-17 13:13:22 +00:00
|
|
|
*/
|
2018-08-27 16:56:28 +00:00
|
|
|
public static BlockState get(@Nullable BlockType type, String state, BlockState defaultState) throws InputParseException {
|
2018-08-12 14:03:07 +00:00
|
|
|
int propStrStart = state.indexOf('[');
|
|
|
|
if (type == null) {
|
|
|
|
CharSequence key;
|
|
|
|
if (propStrStart == -1) {
|
|
|
|
key = state;
|
|
|
|
} else {
|
|
|
|
MutableCharSequence charSequence = MutableCharSequence.getTemporal();
|
|
|
|
charSequence.setString(state);
|
|
|
|
charSequence.setSubstring(0, propStrStart);
|
|
|
|
key = charSequence;
|
2018-07-17 14:42:09 +00:00
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
type = BlockTypes.get(key);
|
2018-08-21 17:22:37 +00:00
|
|
|
if (type == null) {
|
|
|
|
String input = key.toString();
|
2018-10-03 23:12:29 +00:00
|
|
|
throw new SuggestInputParseException("Does not match a valid block type: " + input, input, () -> Stream.of(BlockTypes.values)
|
2019-04-11 11:32:32 +00:00
|
|
|
.filter(b -> StringMan.blockStateMatches(input, b.getId()))
|
2019-06-04 15:48:30 +00:00
|
|
|
.map(BlockType::getId)
|
2019-04-11 11:32:32 +00:00
|
|
|
.sorted(StringMan.blockStateComparator(input))
|
2018-08-21 17:22:37 +00:00
|
|
|
.collect(Collectors.toList())
|
|
|
|
);
|
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
if (propStrStart == -1) {
|
|
|
|
return type.getDefaultState();
|
2018-07-17 14:42:09 +00:00
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
List<? extends Property> propList = type.getProperties();
|
|
|
|
|
2018-08-21 17:22:37 +00:00
|
|
|
if (state.charAt(state.length() - 1) != ']') state = state + "]";
|
2018-08-12 14:03:07 +00:00
|
|
|
MutableCharSequence charSequence = MutableCharSequence.getTemporal();
|
|
|
|
charSequence.setString(state);
|
|
|
|
|
|
|
|
if (propList.size() == 1) {
|
|
|
|
AbstractProperty property = (AbstractProperty) propList.get(0);
|
|
|
|
String name = property.getName();
|
|
|
|
|
|
|
|
charSequence.setSubstring(propStrStart + name.length() + 2, state.length() - 1);
|
2019-04-23 05:08:05 +00:00
|
|
|
int index = charSequence.length() <= 0 ? -1 : property.getIndexFor(charSequence);
|
|
|
|
if (index != -1) {
|
|
|
|
return type.withPropertyId(index);
|
|
|
|
}
|
2018-07-17 14:42:09 +00:00
|
|
|
}
|
2018-08-27 16:56:28 +00:00
|
|
|
int stateId;
|
|
|
|
if (defaultState != null) {
|
|
|
|
stateId = defaultState.getInternalId();
|
|
|
|
} else {
|
|
|
|
stateId = type.getInternalId();
|
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
int length = state.length();
|
|
|
|
AbstractProperty property = null;
|
|
|
|
|
|
|
|
int last = propStrStart + 1;
|
|
|
|
for (int i = last; i < length; i++) {
|
|
|
|
char c = state.charAt(i);
|
|
|
|
switch (c) {
|
|
|
|
case ']':
|
|
|
|
case ',': {
|
2018-08-21 17:22:37 +00:00
|
|
|
charSequence.setSubstring(last, i);
|
2018-08-14 10:57:32 +00:00
|
|
|
if (property != null) {
|
|
|
|
int index = property.getIndexFor(charSequence);
|
2018-08-21 17:22:37 +00:00
|
|
|
if (index == -1) {
|
2019-04-23 05:08:05 +00:00
|
|
|
throw SuggestInputParseException.of(charSequence.toString(), property.getValues());
|
2018-08-21 17:22:37 +00:00
|
|
|
}
|
2018-08-14 10:57:32 +00:00
|
|
|
stateId = property.modifyIndex(stateId, index);
|
|
|
|
} else {
|
2018-08-21 17:22:37 +00:00
|
|
|
// suggest
|
|
|
|
PropertyKey key = PropertyKey.get(charSequence);
|
|
|
|
if (key == null || !type.hasProperty(key)) {
|
|
|
|
// Suggest property
|
|
|
|
String input = charSequence.toString();
|
|
|
|
BlockType finalType = type;
|
2019-04-05 04:15:10 +00:00
|
|
|
throw new SuggestInputParseException("Invalid property " + charSequence + ":" + input + " for type " + type, input, () ->
|
2018-08-21 17:22:37 +00:00
|
|
|
finalType.getProperties().stream()
|
2019-06-04 15:48:30 +00:00
|
|
|
.map(Property::getName)
|
2019-04-11 11:32:32 +00:00
|
|
|
.filter(p -> StringMan.blockStateMatches(input, p))
|
|
|
|
.sorted(StringMan.blockStateComparator(input))
|
2018-08-21 17:22:37 +00:00
|
|
|
.collect(Collectors.toList()));
|
|
|
|
} else {
|
2019-06-04 15:48:30 +00:00
|
|
|
throw new SuggestInputParseException("No operator for " + state, "", () -> Collections.singletonList("="));
|
2018-08-21 17:22:37 +00:00
|
|
|
}
|
2018-08-14 10:57:32 +00:00
|
|
|
}
|
2018-08-21 17:22:37 +00:00
|
|
|
property = null;
|
2018-08-12 14:03:07 +00:00
|
|
|
last = i + 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case '=': {
|
|
|
|
charSequence.setSubstring(last, i);
|
|
|
|
property = (AbstractProperty) type.getPropertyMap().get(charSequence);
|
|
|
|
last = i + 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
}
|
2018-07-17 14:42:09 +00:00
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
return type.withPropertyId(stateId >> BlockTypes.BIT_OFFSET);
|
|
|
|
}
|
2018-07-17 14:42:09 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
@Override
|
|
|
|
public BlockState withPropertyId(int propertyId) {
|
|
|
|
return getBlockType().withPropertyId(propertyId);
|
2018-07-17 14:42:09 +00:00
|
|
|
}
|
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
@Override
|
|
|
|
public BlockType getBlockType() {
|
|
|
|
return this.blockType;
|
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
@Override
|
2019-01-09 07:13:44 +00:00
|
|
|
public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException {
|
2018-08-12 14:03:07 +00:00
|
|
|
return extent.setBlock(set, this);
|
|
|
|
}
|
2018-06-17 05:42:47 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
@Override
|
2018-12-27 00:39:10 +00:00
|
|
|
public BaseBlock apply(BlockVector3 position) {
|
|
|
|
return this.toBaseBlock();
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The internal id with no type information
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
@Deprecated
|
|
|
|
@Override
|
|
|
|
public final int getInternalPropertiesId() {
|
|
|
|
return this.getInternalId() >> BlockTypes.BIT_OFFSET;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Deprecated
|
|
|
|
@Override
|
|
|
|
public final int getInternalBlockTypeId() {
|
|
|
|
return this.getInternalId() & BlockTypes.BIT_MASK;
|
2018-06-17 05:42:47 +00:00
|
|
|
}
|
|
|
|
|
2018-06-18 07:53:33 +00:00
|
|
|
@Override
|
2018-07-05 23:16:52 +00:00
|
|
|
public <V> BlockState with(final Property<V> property, final V value) {
|
2018-08-12 14:03:07 +00:00
|
|
|
try {
|
2019-01-31 15:08:58 +00:00
|
|
|
BlockType type = getBlockType();
|
2018-08-12 14:03:07 +00:00
|
|
|
int newState = ((AbstractProperty) property).modify(this.getInternalId(), value);
|
2018-08-27 16:56:28 +00:00
|
|
|
return newState != this.getInternalId() ? type.withStateId(newState) : this;
|
2018-08-12 14:03:07 +00:00
|
|
|
} catch (ClassCastException e) {
|
|
|
|
throw new IllegalArgumentException("Property not found: " + property);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-06 00:46:48 +00:00
|
|
|
@Override
|
|
|
|
public <V> V getState(final Property<V> property) {
|
|
|
|
try {
|
|
|
|
AbstractProperty ap = (AbstractProperty) property;
|
|
|
|
return (V) ap.getValue(this.getInternalId());
|
|
|
|
} catch (ClassCastException e) {
|
|
|
|
throw new IllegalArgumentException("Property not found: " + property);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
@Override
|
|
|
|
public <V> BlockState with(final PropertyKey property, final V value) {
|
|
|
|
try {
|
2019-01-31 15:08:58 +00:00
|
|
|
BlockType type = getBlockType();
|
2018-08-27 16:56:28 +00:00
|
|
|
int newState = ((AbstractProperty) type.getProperty(property)).modify(this.getInternalId(), value);
|
|
|
|
return newState != this.getInternalId() ? type.withStateId(newState) : this;
|
2018-08-12 14:03:07 +00:00
|
|
|
} catch (ClassCastException e) {
|
|
|
|
throw new IllegalArgumentException("Property not found: " + property);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
@Override
|
2019-07-06 00:46:48 +00:00
|
|
|
public Map<Property<?>, Object> getStates() {
|
2019-06-04 15:48:30 +00:00
|
|
|
BlockType type = this.getBlockType();
|
|
|
|
// Lazily initialize the map
|
|
|
|
Map<? extends Property, Object> map = Maps.asMap(type.getPropertiesSet(), (Function<Property, Object>) this::getState);
|
|
|
|
return (Map<Property<?>, Object>) map;
|
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
@Override
|
2019-07-06 00:46:48 +00:00
|
|
|
public boolean equalsFuzzy(BlockStateHolder<?> o) {
|
|
|
|
if (null == o) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (this == o) {
|
|
|
|
// Added a reference equality check for speediness
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (o.getClass() == BlockState.class) {
|
|
|
|
return o.getOrdinal() == this.getOrdinal();
|
2018-06-17 13:13:22 +00:00
|
|
|
}
|
2019-07-06 00:46:48 +00:00
|
|
|
return o.equalsFuzzy(this);
|
2018-06-17 05:42:47 +00:00
|
|
|
}
|
|
|
|
|
2018-06-18 07:53:33 +00:00
|
|
|
@Override
|
2019-06-04 15:48:30 +00:00
|
|
|
public BlockState toImmutableState() {
|
|
|
|
return this;
|
2018-06-17 05:42:47 +00:00
|
|
|
}
|
|
|
|
|
2018-06-18 07:53:33 +00:00
|
|
|
@Override
|
2019-06-04 15:48:30 +00:00
|
|
|
public BaseBlock toBaseBlock() {
|
|
|
|
return this.emptyBaseBlock;
|
2018-06-17 13:13:22 +00:00
|
|
|
}
|
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
@Deprecated
|
2019-01-31 15:08:58 +00:00
|
|
|
@Override
|
2019-07-06 00:46:48 +00:00
|
|
|
public <V> V getState(PropertyKey key) {
|
2019-06-04 15:48:30 +00:00
|
|
|
return getState(getBlockType().getProperty(key));
|
2019-01-31 15:08:58 +00:00
|
|
|
}
|
2018-08-10 10:29:06 +00:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public BaseBlock toBaseBlock(CompoundTag compoundTag) {
|
|
|
|
if (compoundTag == null) {
|
|
|
|
return toBaseBlock();
|
|
|
|
}
|
|
|
|
return new BaseBlock(this, compoundTag);
|
|
|
|
}
|
|
|
|
|
2019-01-31 15:08:58 +00:00
|
|
|
@Override
|
|
|
|
public int getInternalId() {
|
2019-04-05 06:51:42 +00:00
|
|
|
return internalId;
|
2019-01-31 15:08:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public BlockMaterial getMaterial() {
|
2019-04-03 13:25:16 +00:00
|
|
|
if (this.material == null) {
|
|
|
|
if (blockType == BlockTypes.__RESERVED__) {
|
|
|
|
return this.material = blockType.getMaterial();
|
|
|
|
}
|
2019-06-04 15:48:30 +00:00
|
|
|
this.material = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry().getMaterial(this);
|
2019-04-03 13:25:16 +00:00
|
|
|
}
|
|
|
|
return material;
|
2019-01-31 15:08:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getOrdinal() {
|
2019-04-03 13:25:16 +00:00
|
|
|
return this.ordinal;
|
2019-01-31 15:08:58 +00:00
|
|
|
}
|
2019-04-03 13:25:16 +00:00
|
|
|
|
2018-07-23 02:48:11 +00:00
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return getAsString();
|
|
|
|
}
|
2018-08-19 07:53:29 +00:00
|
|
|
|
2018-12-27 05:19:58 +00:00
|
|
|
@Override
|
|
|
|
public boolean equals(Object obj) {
|
|
|
|
if (!(obj instanceof BlockState)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return equalsFuzzy((BlockState) obj);
|
|
|
|
}
|
2018-08-19 07:53:29 +00:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public int hashCode() {
|
2019-04-03 05:53:34 +00:00
|
|
|
return getOrdinal();
|
2018-08-19 07:53:29 +00:00
|
|
|
}
|
2018-06-17 05:42:47 +00:00
|
|
|
}
|