Basically finish the state system. Just 1 more bug that I'm encountering.

This commit is contained in:
Matthew Miller 2018-07-18 00:42:09 +10:00
parent 4938f419ad
commit 6b5f218809
21 changed files with 158 additions and 98 deletions

View File

@ -28,7 +28,6 @@ import com.sk89q.worldedit.world.registry.PassthroughBlockMaterial;
import org.bukkit.Material;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
@ -39,17 +38,9 @@ public class BukkitBlockRegistry extends BundledBlockRegistry {
@Nullable
@Override
public BlockMaterial getMaterial(String id) {
return materialMap.computeIfAbsent(BukkitUtil.toMaterial(BlockTypes.get(id)),
material -> new BukkitBlockMaterial(BukkitBlockRegistry.super.getMaterial(id), material));
}
@Override
public List<Object> getPropertyValues(BlockType blockType, Property<?> property) {
if (WorldEditPlugin.getInstance().getBukkitImplAdapter() != null) {
return WorldEditPlugin.getInstance().getBukkitImplAdapter().getPropertyValues(blockType, property);
}
return super.getPropertyValues(blockType, property);
public BlockMaterial getMaterial(BlockType blockType) {
return materialMap.computeIfAbsent(BukkitUtil.toMaterial(blockType),
material -> new BukkitBlockMaterial(BukkitBlockRegistry.super.getMaterial(blockType), material));
}
@Nullable

View File

@ -82,12 +82,13 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
WorldEdit worldEdit = WorldEdit.getInstance();
loadConfig(); // Load configuration
PermissionsResolverManager.initialize(this); // Setup permission resolver
// Setup platform
server = new BukkitServerInterface(this, getServer());
worldEdit.getPlatformManager().register(server);
worldEdit.loadMappings();
loadConfig(); // Load configuration
PermissionsResolverManager.initialize(this); // Setup permission resolver
// Register CUI
getServer().getMessenger().registerIncomingPluginChannel(this, CUI_PLUGIN_CHANNEL, new CUIChannelListener(this));

View File

@ -28,7 +28,6 @@ import org.bukkit.Location;
import org.bukkit.block.Biome;
import org.bukkit.entity.Entity;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
@ -95,15 +94,6 @@ public interface BukkitImplAdapter {
@Nullable
Entity createEntity(Location location, BaseEntity state);
/**
* Get a list of values for a property.
*
* @param property The property
* @return The list of values
*/
List<Object> getPropertyValues(BlockType blockType, Property<?> property);
/**
* Get a map of string -> properties
*

View File

@ -100,9 +100,6 @@ public class WorldEdit {
static {
WorldEditPrefixHandler.register("com.sk89q.worldedit");
getVersion();
BundledBlockData.getInstance(); // Load block registry
BundledItemData.getInstance(); // Load item registry
LegacyMapper.getInstance(); // Load item registry
}
private WorldEdit() {
@ -283,6 +280,15 @@ public class WorldEdit {
}
}
/**
* Load the bundled mappings.
*/
public void loadMappings() {
BundledBlockData.getInstance(); // Load block registry
BundledItemData.getInstance(); // Load item registry
LegacyMapper.getInstance(); // Load item registry
}
/**
* Checks to see if the specified radius is within bounds.
*

View File

@ -45,7 +45,6 @@ import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
@ -78,7 +77,6 @@ class DefaultBlockParser extends InputParser<BlockStateHolder> {
public BlockStateHolder parseFromInput(String input, ParserContext context)
throws InputParseException {
String originalInput = input;
input = input.replace("_", " ");
input = input.replace(";", "|");
Exception suppressed = null;
try {
@ -162,8 +160,7 @@ class DefaultBlockParser extends InputParser<BlockStateHolder> {
throw new NoMatchException("Bad state format in " + parseableData);
}
Property propertyKey = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS)
.getRegistries().getBlockRegistry().getProperties(state.getBlockType()).get(parts[0]);
Property propertyKey = state.getBlockType().getPropertyMap().get(parts[0]);
if (propertyKey == null) {
throw new NoMatchException("Unknown state " + parts[0] + " for block " + state.getBlockType().getName());
}
@ -176,6 +173,7 @@ class DefaultBlockParser extends InputParser<BlockStateHolder> {
} catch (NoMatchException e) {
throw e; // Pass-through
} catch (Exception e) {
e.printStackTrace();
throw new NoMatchException("Unknown state '" + parseableData + "'");
}
}

View File

@ -165,6 +165,9 @@ public class PlatformManager {
if (platform != null) {
return platform;
} else {
if (preferences.isEmpty()) {
return platforms.get(0); // Use the first available if preferences have not been decided yet.
}
throw new NoCapablePlatformException("No platform was found supporting " + capability.name());
}
}

View File

@ -22,20 +22,17 @@ package com.sk89q.worldedit.extent.transform;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.LazyBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.registry.state.DirectionalProperty;
import com.sk89q.worldedit.registry.state.Property;
import java.util.Map;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import javax.annotation.Nullable;
@ -127,14 +124,7 @@ public class BlockTransformExtent extends AbstractDelegateExtent {
checkNotNull(block);
checkNotNull(transform);
Map<String, ? extends Property> states = WorldEdit.getInstance().getPlatformManager()
.queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry().getProperties(block.getBlockType());
if (states == null) {
return changedBlock;
}
for (Property property : states.values()) {
for (Property property : block.getBlockType().getProperties()) {
if (property instanceof DirectionalProperty) {
Vector value = (Vector) block.getState(property);
if (value != null) {
@ -164,11 +154,11 @@ public class BlockTransformExtent extends AbstractDelegateExtent {
double closest = -2;
boolean found = false;
for (Vector v : state.getValues()) {
double dot = v.normalize().dot(newDirection);
for (Direction v : state.getValues()) {
double dot = v.toVector().normalize().dot(newDirection);
if (dot >= closest) {
closest = dot;
newValue = v;
newValue = v.toVector();
found = true;
}
}

View File

@ -21,15 +21,21 @@ package com.sk89q.worldedit.registry.state;
import static com.google.common.base.Preconditions.checkState;
import java.util.List;
public abstract class AbstractProperty<T> implements Property<T> {
private String name;
private List<T> values;
public AbstractProperty() {
public AbstractProperty(final String name, final List<T> values) {
this.name = name;
this.values = values;
}
public AbstractProperty(final String name) {
this.name = name;
@Override
public List<T> getValues() {
return this.values;
}
@Override

View File

@ -25,14 +25,17 @@ import javax.annotation.Nullable;
public class BooleanProperty extends AbstractProperty<Boolean> {
@Override
public List<Boolean> getValues() {
return null;
public BooleanProperty(final String name, final List<Boolean> values) {
super(name, values);
}
@Nullable
@Override
public Boolean getValueFor(String string) {
return null;
boolean val = Boolean.parseBoolean(string);
if (!getValues().contains(val)) {
throw new IllegalArgumentException("Invalid boolean value: " + string + ". Must be in " + getValues().toString());
}
return val;
}
}

View File

@ -19,21 +19,25 @@
package com.sk89q.worldedit.registry.state;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.util.Direction;
import java.util.List;
import javax.annotation.Nullable;
public class DirectionalProperty extends AbstractProperty<Vector> {
@Override
public List<Vector> getValues() {
return null;
public class DirectionalProperty extends AbstractProperty<Direction> {
public DirectionalProperty(final String name, final List<Direction> values) {
super(name, values);
}
@Nullable
@Override
public Vector getValueFor(final String string) {
return null;
public Direction getValueFor(final String string) {
Direction direction = Direction.valueOf(string);
if (!getValues().contains(direction)) {
throw new IllegalArgumentException("Invalid direction value: " + string + ". Must be in " + getValues().toString());
}
return direction;
}
}

View File

@ -25,14 +25,16 @@ import javax.annotation.Nullable;
public class EnumProperty extends AbstractProperty<String> {
@Override
public List<String> getValues() {
return null;
public EnumProperty(final String name, final List<String> values) {
super(name, values);
}
@Nullable
@Override
public String getValueFor(String string) {
return null;
if (!getValues().contains(string)) {
throw new IllegalArgumentException("Invalid value: " + string + ". Must be in " + getValues().toString());
}
return string;
}
}

View File

@ -25,14 +25,21 @@ import javax.annotation.Nullable;
public class IntegerProperty extends AbstractProperty<Integer> {
@Override
public List<Integer> getValues() {
return null;
public IntegerProperty(final String name, final List<Integer> values) {
super(name, values);
}
@Nullable
@Override
public Integer getValueFor(String string) {
return null;
try {
int val = Integer.parseInt(string);
if (!getValues().contains(val)) {
throw new IllegalArgumentException("Invalid int value: " + string + ". Must be in " + getValues().toString());
}
return val;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid int value: " + string + ". Not an int.");
}
}
}

View File

@ -50,7 +50,8 @@ public interface Property<T> {
*
* @param string The string
* @return The value, or null
* @throws IllegalArgumentException When the value is invalid.
*/
@Nullable
T getValueFor(String string);
T getValueFor(String string) throws IllegalArgumentException;
}

View File

@ -21,6 +21,7 @@ package com.sk89q.worldedit.world.block;
import com.google.common.collect.ArrayTable;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Table;
import com.sk89q.worldedit.registry.state.Property;
@ -28,8 +29,10 @@ import com.sk89q.worldedit.registry.state.Property;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* An immutable class that represents the state a block can be in.
@ -46,7 +49,7 @@ public class BlockState implements BlockStateHolder<BlockState> {
BlockState(BlockType blockType) {
this.blockType = blockType;
this.values = new HashMap<>();
this.values = new LinkedHashMap<>();
this.fuzzy = false;
}
@ -62,6 +65,34 @@ public class BlockState implements BlockStateHolder<BlockState> {
this.fuzzy = true;
}
public static Map<Map<Property<?>, Object>, BlockState> generateStateMap(BlockType blockType) {
List<? extends Property> properties = blockType.getProperties();
List<List<Object>> valueLists = Lists.cartesianProduct(properties.stream().map(Property::getValues).collect(Collectors.toList()));
Map<Map<Property<?>, Object>, BlockState> stateMap = new LinkedHashMap<>();
for (int i = 0; i < valueLists.size(); i++) {
List<Object> valueList = valueLists.get(i);
Property<?> property = properties.get(i);
LinkedHashMap<Property<?>, Object> valueMap = new LinkedHashMap<>();
BlockState stateMaker = new BlockState(blockType);
for (Object value : valueList) {
valueMap.put(property, value);
stateMaker.setState(property, value);
}
stateMap.put(valueMap, stateMaker);
}
if (stateMap.isEmpty()) {
// No properties.
stateMap.put(new LinkedHashMap<>(), new BlockState(blockType));
}
for (BlockState state : stateMap.values()) {
state.populate(stateMap);
}
return stateMap;
}
public void populate(Map<Map<Property<?>, Object>, BlockState> stateMap) {
final Table<Property<?>, Object, BlockState> states = HashBasedTable.create();
@ -154,7 +185,7 @@ public class BlockState implements BlockStateHolder<BlockState> {
* @param value The value
* @return The blockstate, for chaining
*/
private <V> BlockState setState(final Property<V> property, final V value) {
private BlockState setState(final Property<?> property, final Object value) {
this.values.put(property, value);
return this;
}

View File

@ -19,15 +19,21 @@
package com.sk89q.worldedit.world.block;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.blocks.BlockMaterial;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.registry.NamespacedRegistry;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.item.ItemTypes;
import com.sk89q.worldedit.world.registry.BundledBlockData;
import com.sk89q.worldedit.world.registry.LegacyMapper;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import javax.annotation.Nullable;
@ -38,6 +44,8 @@ public class BlockType {
private String id;
private BlockState defaultState;
private Map<String, ? extends Property> properties;
private BlockMaterial blockMaterial;
public BlockType(String id) {
this(id, null);
@ -49,7 +57,7 @@ public class BlockType {
id = "minecraft:" + id;
}
this.id = id;
this.defaultState = new BlockState(this);
this.defaultState = new ArrayList<>(BlockState.generateStateMap(this).values()).get(0);
if (values != null) {
this.defaultState = values.apply(this.defaultState);
}
@ -78,6 +86,28 @@ public class BlockType {
}
}
/**
* Gets the properties of this BlockType in a key->property mapping.
*
* @return The properties map
*/
public Map<String, ? extends Property> getPropertyMap() {
if (properties == null) {
properties = ImmutableMap.copyOf(WorldEdit.getInstance().getPlatformManager()
.queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry().getProperties(this));
}
return this.properties;
}
/**
* Gets the properties of this BlockType.
*
* @return the properties
*/
public List<? extends Property> getProperties() {
return ImmutableList.copyOf(this.getPropertyMap().values());
}
/**
* Gets the default state of this block type.
*
@ -112,7 +142,10 @@ public class BlockType {
* @return The material
*/
public BlockMaterial getMaterial() {
return WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry().getMaterial(this.id);
if (this.blockMaterial == null) {
this.blockMaterial = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry().getMaterial(this);
}
return this.blockMaterial;
}
/**

View File

@ -36,20 +36,11 @@ public interface BlockRegistry {
/**
* Get the material for the given block.
*
* @param id the block
* @param blockType the block
* @return the material, or null if the material information is not known
*/
@Nullable
BlockMaterial getMaterial(String id);
/**
* Get an unmodifiable list of values for this property.
*
* @param blockType The block
* @param property the property
* @return the list of values
*/
List<Object> getPropertyValues(BlockType blockType, Property<?> property);
BlockMaterial getMaterial(BlockType blockType);
/**
* Get an unmodifiable map of states for this block.

View File

@ -52,7 +52,7 @@ import javax.annotation.Nullable;
public class BundledBlockData {
private static final Logger log = Logger.getLogger(BundledBlockData.class.getCanonicalName());
private static final BundledBlockData INSTANCE = new BundledBlockData();
private static BundledBlockData INSTANCE;
private final Map<String, BlockEntry> idMap = new HashMap<>();
@ -125,6 +125,9 @@ public class BundledBlockData {
* @return the instance
*/
public static BundledBlockData getInstance() {
if (INSTANCE == null) {
INSTANCE = new BundledBlockData();
}
return INSTANCE;
}

View File

@ -24,7 +24,6 @@ import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.world.block.BlockType;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
@ -37,13 +36,8 @@ public class BundledBlockRegistry implements BlockRegistry {
@Nullable
@Override
public BlockMaterial getMaterial(String id) {
return new PassthroughBlockMaterial(BundledBlockData.getInstance().getMaterialById(id));
}
@Override
public List<Object> getPropertyValues(BlockType blockType, Property<?> property) {
return Collections.emptyList(); // Oof
public BlockMaterial getMaterial(BlockType blockType) {
return new PassthroughBlockMaterial(BundledBlockData.getInstance().getMaterialById(blockType.getId()));
}
@Nullable

View File

@ -51,7 +51,7 @@ import javax.annotation.Nullable;
public class BundledItemData {
private static final Logger log = Logger.getLogger(BundledItemData.class.getCanonicalName());
private static final BundledItemData INSTANCE = new BundledItemData();
private static BundledItemData INSTANCE;
private final Map<String, ItemEntry> idMap = new HashMap<>();
@ -108,6 +108,9 @@ public class BundledItemData {
* @return the instance
*/
public static BundledItemData getInstance() {
if (INSTANCE == null) {
INSTANCE = new BundledItemData();
}
return INSTANCE;
}

View File

@ -46,7 +46,7 @@ import javax.annotation.Nullable;
public class LegacyMapper {
private static final Logger log = Logger.getLogger(LegacyMapper.class.getCanonicalName());
private static final LegacyMapper INSTANCE = new LegacyMapper();
private static LegacyMapper INSTANCE;
private BiMap<String, BlockState> blockMap = HashBiMap.create();
private BiMap<String, ItemType> itemMap = HashBiMap.create();
@ -141,6 +141,9 @@ public class LegacyMapper {
}
public static LegacyMapper getInstance() {
if (INSTANCE == null) {
INSTANCE = new LegacyMapper();
}
return INSTANCE;
}