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 org.bukkit.Material;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -39,17 +38,9 @@ public class BukkitBlockRegistry extends BundledBlockRegistry {
@Nullable @Nullable
@Override @Override
public BlockMaterial getMaterial(String id) { public BlockMaterial getMaterial(BlockType blockType) {
return materialMap.computeIfAbsent(BukkitUtil.toMaterial(BlockTypes.get(id)), return materialMap.computeIfAbsent(BukkitUtil.toMaterial(blockType),
material -> new BukkitBlockMaterial(BukkitBlockRegistry.super.getMaterial(id), material)); material -> new BukkitBlockMaterial(BukkitBlockRegistry.super.getMaterial(blockType), 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);
} }
@Nullable @Nullable

View File

@ -82,12 +82,13 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
WorldEdit worldEdit = WorldEdit.getInstance(); WorldEdit worldEdit = WorldEdit.getInstance();
loadConfig(); // Load configuration
PermissionsResolverManager.initialize(this); // Setup permission resolver
// Setup platform // Setup platform
server = new BukkitServerInterface(this, getServer()); server = new BukkitServerInterface(this, getServer());
worldEdit.getPlatformManager().register(server); worldEdit.getPlatformManager().register(server);
worldEdit.loadMappings();
loadConfig(); // Load configuration
PermissionsResolverManager.initialize(this); // Setup permission resolver
// Register CUI // Register CUI
getServer().getMessenger().registerIncomingPluginChannel(this, CUI_PLUGIN_CHANNEL, new CUIChannelListener(this)); 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.block.Biome;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -95,15 +94,6 @@ public interface BukkitImplAdapter {
@Nullable @Nullable
Entity createEntity(Location location, BaseEntity state); 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 * Get a map of string -> properties
* *

View File

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

View File

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

View File

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

View File

@ -25,14 +25,17 @@ import javax.annotation.Nullable;
public class BooleanProperty extends AbstractProperty<Boolean> { public class BooleanProperty extends AbstractProperty<Boolean> {
@Override public BooleanProperty(final String name, final List<Boolean> values) {
public List<Boolean> getValues() { super(name, values);
return null;
} }
@Nullable @Nullable
@Override @Override
public Boolean getValueFor(String string) { 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; package com.sk89q.worldedit.registry.state;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.util.Direction;
import java.util.List; import java.util.List;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class DirectionalProperty extends AbstractProperty<Vector> { public class DirectionalProperty extends AbstractProperty<Direction> {
@Override
public List<Vector> getValues() { public DirectionalProperty(final String name, final List<Direction> values) {
return null; super(name, values);
} }
@Nullable @Nullable
@Override @Override
public Vector getValueFor(final String string) { public Direction getValueFor(final String string) {
return null; 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> { public class EnumProperty extends AbstractProperty<String> {
@Override public EnumProperty(final String name, final List<String> values) {
public List<String> getValues() { super(name, values);
return null;
} }
@Nullable @Nullable
@Override @Override
public String getValueFor(String string) { 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> { public class IntegerProperty extends AbstractProperty<Integer> {
@Override public IntegerProperty(final String name, final List<Integer> values) {
public List<Integer> getValues() { super(name, values);
return null;
} }
@Nullable @Nullable
@Override @Override
public Integer getValueFor(String string) { 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 * @param string The string
* @return The value, or null * @return The value, or null
* @throws IllegalArgumentException When the value is invalid.
*/ */
@Nullable @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.ArrayTable;
import com.google.common.collect.HashBasedTable; import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Table; import com.google.common.collect.Table;
import com.sk89q.worldedit.registry.state.Property; 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.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
/** /**
* An immutable class that represents the state a block can be in. * 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) { BlockState(BlockType blockType) {
this.blockType = blockType; this.blockType = blockType;
this.values = new HashMap<>(); this.values = new LinkedHashMap<>();
this.fuzzy = false; this.fuzzy = false;
} }
@ -62,6 +65,34 @@ public class BlockState implements BlockStateHolder<BlockState> {
this.fuzzy = true; 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) { public void populate(Map<Map<Property<?>, Object>, BlockState> stateMap) {
final Table<Property<?>, Object, BlockState> states = HashBasedTable.create(); final Table<Property<?>, Object, BlockState> states = HashBasedTable.create();
@ -154,7 +185,7 @@ public class BlockState implements BlockStateHolder<BlockState> {
* @param value The value * @param value The value
* @return The blockstate, for chaining * @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); this.values.put(property, value);
return this; return this;
} }

View File

@ -19,15 +19,21 @@
package com.sk89q.worldedit.world.block; 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.WorldEdit;
import com.sk89q.worldedit.blocks.BlockMaterial; import com.sk89q.worldedit.blocks.BlockMaterial;
import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.registry.NamespacedRegistry; 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.ItemType;
import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.item.ItemTypes;
import com.sk89q.worldedit.world.registry.BundledBlockData; import com.sk89q.worldedit.world.registry.BundledBlockData;
import com.sk89q.worldedit.world.registry.LegacyMapper; 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 java.util.function.Function;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -38,6 +44,8 @@ public class BlockType {
private String id; private String id;
private BlockState defaultState; private BlockState defaultState;
private Map<String, ? extends Property> properties;
private BlockMaterial blockMaterial;
public BlockType(String id) { public BlockType(String id) {
this(id, null); this(id, null);
@ -49,7 +57,7 @@ public class BlockType {
id = "minecraft:" + id; id = "minecraft:" + id;
} }
this.id = id; this.id = id;
this.defaultState = new BlockState(this); this.defaultState = new ArrayList<>(BlockState.generateStateMap(this).values()).get(0);
if (values != null) { if (values != null) {
this.defaultState = values.apply(this.defaultState); 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. * Gets the default state of this block type.
* *
@ -112,7 +142,10 @@ public class BlockType {
* @return The material * @return The material
*/ */
public BlockMaterial getMaterial() { 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. * 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 * @return the material, or null if the material information is not known
*/ */
@Nullable @Nullable
BlockMaterial getMaterial(String id); BlockMaterial getMaterial(BlockType blockType);
/**
* 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);
/** /**
* Get an unmodifiable map of states for this block. * Get an unmodifiable map of states for this block.

View File

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

View File

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

View File

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

View File

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