Major command changes that don't work yet.

This commit is contained in:
MattBDev
2019-07-05 20:46:48 -04:00
parent ffc2092d93
commit 8108d0a936
399 changed files with 13558 additions and 7985 deletions

View File

@ -26,7 +26,6 @@ import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.internal.registry.AbstractFactory;
import com.sk89q.worldedit.world.block.BaseBlock;
import java.util.HashSet;
import java.util.Set;
@ -45,9 +44,7 @@ public class BlockFactory extends AbstractFactory<BaseBlock> {
* @param worldEdit the WorldEdit instance.
*/
public BlockFactory(WorldEdit worldEdit) {
super(worldEdit);
register(new DefaultBlockParser(worldEdit));
super(worldEdit, new DefaultBlockParser(worldEdit));
}
/**
@ -61,7 +58,7 @@ public class BlockFactory extends AbstractFactory<BaseBlock> {
public Set<BaseBlock> parseFromListInput(String input, ParserContext context) throws InputParseException {
Set<BaseBlock> blocks = new HashSet<>();
String[] splits = input.split(",");
for (String token : StringUtil.parseListInQuotes(splits, ',', '[', ']')) {
for (String token : StringUtil.parseListInQuotes(splits, ',', '[', ']', true)) {
blocks.add(parseFromInput(token, context));
}
return blocks;

View File

@ -32,9 +32,7 @@ public class ItemFactory extends AbstractFactory<BaseItem> {
* @param worldEdit the WorldEdit instance.
*/
public ItemFactory(WorldEdit worldEdit) {
super(worldEdit);
register(new DefaultItemParser(worldEdit));
super(worldEdit, new DefaultItemParser(worldEdit));
}
}

View File

@ -20,12 +20,16 @@
package com.sk89q.worldedit.extension.factory;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.factory.parser.mask.BiomeMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.BlockCategoryMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.DefaultMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.BlockStateMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.BlocksMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.ExistingMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.ExpressionMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.LazyRegionMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.NegateMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.NoiseMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.OffsetMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.RegionMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.SolidMaskParser;
import com.sk89q.worldedit.extension.input.InputParseException;
@ -54,9 +58,20 @@ public final class MaskFactory extends AbstractFactory<Mask> {
* @param worldEdit the WorldEdit instance
*/
public MaskFactory(WorldEdit worldEdit) {
super(worldEdit);
super(worldEdit, new BlocksMaskParser(worldEdit));
register(new ExistingMaskParser(worldEdit));
register(new SolidMaskParser(worldEdit));
register(new LazyRegionMaskParser(worldEdit));
register(new RegionMaskParser(worldEdit));
register(new OffsetMaskParser(worldEdit));
register(new NoiseMaskParser(worldEdit));
register(new BlockStateMaskParser(worldEdit));
register(new NegateMaskParser(worldEdit));
register(new ExpressionMaskParser(worldEdit));
register(new BlockCategoryMaskParser(worldEdit));
register(new DefaultMaskParser(worldEdit));
register(new BiomeMaskParser(worldEdit));
}
@Override

View File

@ -22,7 +22,6 @@ package com.sk89q.worldedit.extension.factory;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.factory.parser.pattern.BlockCategoryPatternParser;
import com.sk89q.worldedit.extension.factory.parser.pattern.ClipboardPatternParser;
import com.sk89q.worldedit.extension.factory.parser.pattern.DefaultPatternParser;
import com.sk89q.worldedit.extension.factory.parser.pattern.RandomPatternParser;
import com.sk89q.worldedit.extension.factory.parser.pattern.RandomStatePatternParser;
import com.sk89q.worldedit.extension.factory.parser.pattern.SingleBlockPatternParser;
@ -45,9 +44,16 @@ public final class PatternFactory extends AbstractFactory<Pattern> {
* @param worldEdit the WorldEdit instance
*/
public PatternFactory(WorldEdit worldEdit) {
super(worldEdit);
super(worldEdit, new SingleBlockPatternParser(worldEdit));
// split and parse each sub-pattern
register(new RandomPatternParser(worldEdit));
// individual patterns
register(new ClipboardPatternParser(worldEdit));
register(new TypeOrStateApplyingPatternParser(worldEdit));
register(new RandomStatePatternParser(worldEdit));
register(new BlockCategoryPatternParser(worldEdit));
register(new DefaultPatternParser(worldEdit));
}
}

View File

@ -19,23 +19,18 @@
package com.sk89q.worldedit.extension.factory.parser;
import com.boydti.fawe.command.SuggestInputParseException;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.jnbt.JSON2NBT;
import com.boydti.fawe.jnbt.NBTException;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.StringMan;
import com.google.common.collect.Maps;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.NotABlockException;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseItem;
import com.sk89q.worldedit.blocks.MobSpawnerBlock;
import com.sk89q.worldedit.blocks.SignBlock;
import com.sk89q.worldedit.blocks.SkullBlock;
import com.sk89q.worldedit.blocks.metadata.MobType;
import com.sk89q.worldedit.command.util.SuggestionHelper;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.input.DisallowedUsageException;
import com.sk89q.worldedit.extension.input.InputParseException;
@ -43,24 +38,26 @@ import com.sk89q.worldedit.extension.input.NoMatchException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.extent.inventory.SlottableBlockBag;
import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.HandSide;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockCategories;
import com.sk89q.worldedit.world.block.BlockCategory;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.FuzzyBlockState;
import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.entity.EntityTypes;
import com.sk89q.worldedit.world.registry.LegacyMapper;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.stream.Collectors;
import java.util.Map;
import java.util.stream.Stream;
/**
@ -110,6 +107,8 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
}
}
private static String[] EMPTY_STRING_ARRAY = {};
/**
* Backwards compatibility for wool colours in block syntax.
*
@ -161,8 +160,75 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
}
}
private static Map<Property<?>, Object> parseProperties(BlockType type, String[] stateProperties, ParserContext context) throws NoMatchException {
Map<Property<?>, Object> blockStates = new HashMap<>();
if (stateProperties.length > 0) { // Block data not yet detected
// Parse the block data (optional)
for (String parseableData : stateProperties) {
try {
String[] parts = parseableData.split("=");
if (parts.length != 2) {
throw new NoMatchException("Bad state format in " + parseableData);
}
@SuppressWarnings("unchecked")
Property<Object> propertyKey = (Property<Object>) type.getPropertyMap().get(parts[0]);
if (propertyKey == null) {
if (context.getActor() != null) {
throw new NoMatchException("Unknown property " + parts[0] + " for block " + type.getId());
} else {
WorldEdit.logger.warn("Unknown property " + parts[0] + " for block " + type.getId());
}
return Maps.newHashMap();
}
if (blockStates.containsKey(propertyKey)) {
throw new NoMatchException("Duplicate property " + parts[0]);
}
Object value;
try {
value = propertyKey.getValueFor(parts[1]);
} catch (IllegalArgumentException e) {
throw new NoMatchException("Unknown value " + parts[1] + " for state " + parts[0]);
}
blockStates.put(propertyKey, value);
} catch (NoMatchException e) {
throw e; // Pass-through
} catch (Exception e) {
WorldEdit.logger.warn("Unknown state '" + parseableData + "'", e);
throw new NoMatchException("Unknown state '" + parseableData + "'");
}
}
}
return blockStates;
}
@Override
public Stream<String> getSuggestions(String input) {
final int idx = input.lastIndexOf('[');
if (idx < 0) {
return SuggestionHelper.getNamespacedRegistrySuggestions(BlockType.REGISTRY, input);
}
String blockType = input.substring(0, idx);
BlockType type = BlockTypes.get(blockType.toLowerCase(Locale.ROOT));
if (type == null) {
return Stream.empty();
}
String props = input.substring(idx + 1);
if (props.isEmpty()) {
return type.getProperties().stream().map(p -> input + p.getName() + "=");
}
return SuggestionHelper.getBlockPropertySuggestions(blockType, props);
}
private BaseBlock parseLogic(String input, ParserContext context) throws InputParseException {
String[] blockAndExtraData = input.trim().split("\\|", 2);
BlockType blockType = null;
Map<Property<?>, Object> blockStates = new HashMap<>();
String[] blockAndExtraData = input.trim().split("\\|");
blockAndExtraData[0] = woolMapper(blockAndExtraData[0]);
BlockState state = null;
@ -170,31 +236,21 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
// Legacy matcher
if (context.isTryingLegacy()) {
try {
String[] split = blockAndExtraData[0].split(":");
if (split.length == 1) {
String[] split = blockAndExtraData[0].split(":", 2);
if (split.length == 0) {
throw new InputParseException("Invalid colon.");
} else if (split.length == 1) {
state = LegacyMapper.getInstance().getBlockFromLegacy(Integer.parseInt(split[0]));
} else if (MathMan.isInteger(split[0])) {
int id = Integer.parseInt(split[0]);
int data = Integer.parseInt(split[1]);
if (data < 0 || data >= 16) {
throw new InputParseException("Invalid data " + data);
}
state = LegacyMapper.getInstance().getBlockFromLegacy(id, data);
} else {
BlockType type = BlockTypes.get(split[0].toLowerCase());
if (type != null) {
int data = Integer.parseInt(split[1]);
if (data < 0 || data >= 16) {
throw new InputParseException("Invalid data " + data);
}
state = LegacyMapper.getInstance().getBlockFromLegacy(type.getLegacyCombinedId() >> 4, data);
}
state = LegacyMapper.getInstance().getBlockFromLegacy(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
}
} catch (NumberFormatException e) {
if (state != null) {
blockType = state.getBlockType();
}
} catch (NumberFormatException ignored) {
}
}
CompoundTag nbt = null;
if (state == null) {
String typeString;
String stateString = null;
@ -203,139 +259,139 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
typeString = blockAndExtraData[0];
} else {
typeString = blockAndExtraData[0].substring(0, stateStart);
if (stateStart + 1 >= blockAndExtraData[0].length()) {
throw new InputParseException("Invalid format. Hanging bracket @ " + stateStart + ".");
}
int stateEnd = blockAndExtraData[0].lastIndexOf(']');
if (stateEnd < 0) {
throw new InputParseException("Invalid format. Unclosed property.");
}
stateString = blockAndExtraData[0].substring(stateStart + 1, blockAndExtraData[0].length() - 1);
}
if (typeString.isEmpty()) {
throw new InputParseException("Invalid format");
}
// PosX
if (typeString.matches("pos[0-9]+")) {
int index = Integer.parseInt(typeString.replaceAll("[a-z]+", ""));
String[] stateProperties = EMPTY_STRING_ARRAY;
if (stateString != null) {
stateProperties = stateString.split(",");
}
if ("hand".equalsIgnoreCase(typeString)) {
// Get the block type from the item in the user's hand.
final BaseBlock blockInHand = getBlockInHand(context.requireActor(), HandSide.MAIN_HAND).toBaseBlock();
if (blockInHand.getClass() != BaseBlock.class) {
return blockInHand;
}
blockType = blockInHand.getBlockType();
blockStates.putAll(blockInHand.getStates());
} else if ("offhand".equalsIgnoreCase(typeString)) {
// Get the block type from the item in the user's off hand.
final BaseBlock blockInHand = getBlockInHand(context.requireActor(), HandSide.OFF_HAND).toBaseBlock();
if (blockInHand.getClass() != BaseBlock.class) {
return blockInHand;
}
blockType = blockInHand.getBlockType();
blockStates.putAll(blockInHand.getStates());
} else if ("pos1".equalsIgnoreCase(typeString)) {
// Get the block type from the "primary position"
final World world = context.requireWorld();
final BlockVector3 primaryPosition;
try {
primaryPosition = context.requireSession().getRegionSelector(world).getVerticies().get(index - 1);
primaryPosition = context.requireSession().getRegionSelector(world).getPrimaryPosition();
} catch (IncompleteRegionException e) {
throw new InputParseException("Your selection is not complete.");
}
state = world.getBlock(primaryPosition);
final BlockState blockInHand = world.getBlock(primaryPosition);
blockType = blockInHand.getBlockType();
blockStates.putAll(blockInHand.getStates());
} else {
if ("hand".equalsIgnoreCase(typeString)) {
// Get the block type from the item in the user's hand.
state = getBlockInHand(context.requireActor(), HandSide.MAIN_HAND);
} else if ("offhand".equalsIgnoreCase(typeString)) {
// Get the block type from the item in the user's off hand.
state = getBlockInHand(context.requireActor(), HandSide.OFF_HAND);
} else if (typeString.matches("slot[0-9]+")) {
int slot = Integer.parseInt(typeString.substring(4)) - 1;
Actor actor = context.requireActor();
if (!(actor instanceof Player)) {
throw new InputParseException("The user is not a player!");
}
Player player = (Player) actor;
BlockBag bag = player.getInventoryBlockBag();
if (bag == null || !(bag instanceof SlottableBlockBag)) {
throw new InputParseException("Unsupported!");
}
SlottableBlockBag slottable = (SlottableBlockBag) bag;
BaseItem item = slottable.getItem(slot);
if (!item.getType().hasBlockType()) {
throw new InputParseException("You're not holding a block!");
}
state = item.getType().getBlockType().getDefaultState();
nbt = item.getNbtData();
} else {
BlockType type = BlockTypes.parse(typeString.toLowerCase());
if (type != null) {
state = type.getDefaultState();
}
if (state == null) {
throw new NoMatchException("Does not match a valid block type: '" + input + "'");
}
}
// Attempt to lookup a block from ID or name.
blockType = BlockTypes.get(typeString.toLowerCase(Locale.ROOT));
}
if (nbt == null) nbt = state.getNbtData();
if (stateString != null) {
state = BlockState.get(state.getBlockType(), "[" + stateString + "]", state);
if (context.isPreferringWildcard()) {
if (stateString.isEmpty()) {
state = new FuzzyBlockState(state);
} else {
BlockType type = state.getBlockType();
FuzzyBlockState.Builder fuzzyBuilder = FuzzyBlockState.builder();
fuzzyBuilder.type(type);
String[] entries = stateString.split(",");
for (String entry : entries) {
String[] split = entry.split("=");
String key = split[0];
String val = split[1];
Property<Object> prop = type.getProperty(key);
fuzzyBuilder.withProperty(prop, prop.getValueFor(val));
}
state = fuzzyBuilder.build();
}
if (blockType == null) {
throw new NoMatchException("Does not match a valid block type: '" + input + "'");
}
blockStates.putAll(parseProperties(blockType, stateProperties, context));
if (context.isPreferringWildcard()) {
FuzzyBlockState.Builder fuzzyBuilder = FuzzyBlockState.builder();
fuzzyBuilder.type(blockType);
for (Map.Entry<Property<?>, Object> blockState : blockStates.entrySet()) {
@SuppressWarnings("unchecked")
Property<Object> objProp = (Property<Object>) blockState.getKey();
fuzzyBuilder.withProperty(objProp, blockState.getValue());
}
state = fuzzyBuilder.build();
} else {
// No wildcards allowed => eliminate them. (Start with default state)
state = blockType.getDefaultState();
for (Map.Entry<Property<?>, Object> blockState : blockStates.entrySet()) {
@SuppressWarnings("unchecked")
Property<Object> objProp = (Property<Object>) blockState.getKey();
state = state.with(objProp, blockState.getValue());
}
}
}
if (blockAndExtraData.length > 1 && blockAndExtraData[1].startsWith("{")) {
String joined = StringMan.join(Arrays.copyOfRange(blockAndExtraData, 1, blockAndExtraData.length), "|");
try {
nbt = JSON2NBT.getTagFromJson(joined);
} catch (NBTException e) {
throw new NoMatchException(e.getMessage());
}
// this should be impossible but IntelliJ isn't that smart
if (blockType == null) {
throw new NoMatchException("Does not match a valid block type: '" + input + "'");
}
// Check if the item is allowed
BlockType blockType = state.getBlockType();
if (context.isRestricted()) {
Actor actor = context.requireActor();
if (actor != null && !actor.hasPermission("worldedit.anyblock")
&& worldEdit.getConfiguration().disallowedBlocks.contains(blockType.getId())) {
throw new DisallowedUsageException("You are not allowed to use '" + input + "'");
}
}
if (nbt != null) return validate(context, state.toBaseBlock(nbt));
if (!context.isTryingLegacy()) {
return state.toBaseBlock();
}
if (blockType == BlockTypes.SIGN || blockType == BlockTypes.WALL_SIGN) {
if (blockType == BlockTypes.SIGN || blockType == BlockTypes.WALL_SIGN
|| BlockCategories.SIGNS.contains(blockType)) {
// Allow special sign text syntax
String[] text = new String[4];
text[0] = blockAndExtraData.length > 1 ? blockAndExtraData[1] : "";
text[1] = blockAndExtraData.length > 2 ? blockAndExtraData[2] : "";
text[2] = blockAndExtraData.length > 3 ? blockAndExtraData[3] : "";
text[3] = blockAndExtraData.length > 4 ? blockAndExtraData[4] : "";
return validate(context, new SignBlock(state, text));
return new SignBlock(state, text);
} else if (blockType == BlockTypes.SPAWNER) {
// Allow setting mob spawn type
if (blockAndExtraData.length > 1) {
String mobName = blockAndExtraData[1];
for (MobType mobType : MobType.values()) {
if (mobType.getName().toLowerCase().equals(mobName.toLowerCase())) {
mobName = mobType.getName();
break;
}
EntityType ent = EntityTypes.get(mobName.toLowerCase(Locale.ROOT));
if (ent == null) {
throw new NoMatchException("Unknown entity type '" + mobName + "'");
}
mobName = ent.getId();
if (!worldEdit.getPlatformManager().queryCapability(Capability.USER_COMMANDS).isValidMobType(mobName)) {
String finalMobName = mobName.toLowerCase();
throw new SuggestInputParseException("Unknown mob type '" + mobName + "'", mobName, () -> Stream.of(MobType.values())
.map(m -> m.getName().toLowerCase())
.filter(s -> s.startsWith(finalMobName))
.collect(Collectors.toList()));
throw new NoMatchException("Unknown mob type '" + mobName + "'");
}
return validate(context, new MobSpawnerBlock(state, mobName));
return new MobSpawnerBlock(state, mobName);
} else {
return validate(context, new MobSpawnerBlock(state, MobType.PIG.getName()));
//noinspection ConstantConditions
return new MobSpawnerBlock(state, EntityTypes.PIG.getId());
}
} else if (blockType == BlockTypes.PLAYER_HEAD || blockType == BlockTypes.PLAYER_WALL_HEAD) {
// allow setting type/player/rotation
if (blockAndExtraData.length <= 1) {
return validate(context, new SkullBlock(state));
return new SkullBlock(state);
}
String type = blockAndExtraData[1];
return validate(context, new SkullBlock(state, type.replace(" ", "_"))); // valid MC usernames
return new SkullBlock(state, type.replace(" ", "_")); // valid MC usernames
} else {
return validate(context, state.toBaseBlock());
return state.toBaseBlock();
}
}

View File

@ -22,6 +22,7 @@ package com.sk89q.worldedit.extension.factory.parser;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.blocks.BaseItem;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.command.util.SuggestionHelper;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
@ -33,6 +34,7 @@ import com.sk89q.worldedit.world.item.ItemTypes;
import com.sk89q.worldedit.world.registry.LegacyMapper;
import java.util.Locale;
import java.util.stream.Stream;
public class DefaultItemParser extends InputParser<BaseItem> {
@ -40,6 +42,11 @@ public class DefaultItemParser extends InputParser<BaseItem> {
super(worldEdit);
}
@Override
public Stream<String> getSuggestions(String input) {
return SuggestionHelper.getNamespacedRegistrySuggestions(ItemType.REGISTRY, input);
}
@Override
public BaseItem parseFromInput(String input, ParserContext context) throws InputParseException {
BaseItem item = null;
@ -62,6 +69,12 @@ public class DefaultItemParser extends InputParser<BaseItem> {
}
}
if ("hand".equalsIgnoreCase(input)) {
return getItemInHand(context.requireActor(), HandSide.MAIN_HAND);
} else if ("offhand".equalsIgnoreCase(input)) {
return getItemInHand(context.requireActor(), HandSide.OFF_HAND);
}
if (item == null) {
ItemType type = ItemTypes.get(input.toLowerCase(Locale.ROOT));
if (type != null) {
@ -83,4 +96,5 @@ public class DefaultItemParser extends InputParser<BaseItem> {
throw new InputParseException("The user is not a player!");
}
}
}

View File

@ -21,21 +21,21 @@ package com.sk89q.worldedit.extension.factory.parser.mask;
import com.google.common.base.Splitter;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.util.SuggestionHelper;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.function.mask.BiomeMask2D;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.session.request.RequestExtent;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.Biomes;
import com.sk89q.worldedit.world.registry.BiomeRegistry;
import java.util.Collection;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class BiomeMaskParser extends InputParser<Mask> {
@ -43,6 +43,26 @@ public class BiomeMaskParser extends InputParser<Mask> {
super(worldEdit);
}
@Override
public Stream<String> getSuggestions(String input) {
if (input.isEmpty()) {
return Stream.of("$");
}
if (input.charAt(0) == '$') {
input = input.substring(1);
final int lastTermIdx = input.lastIndexOf(',');
if (lastTermIdx <= 0) {
return SuggestionHelper.getNamespacedRegistrySuggestions(BiomeType.REGISTRY, input).map(s -> "$" + s);
}
String prev = input.substring(0, lastTermIdx) + ",";
Set<String> prevBiomes = Arrays.stream(prev.split(",", 0)).collect(Collectors.toSet());
String search = input.substring(lastTermIdx + 1);
return SuggestionHelper.getNamespacedRegistrySuggestions(BiomeType.REGISTRY, search)
.filter(s -> !prevBiomes.contains(s)).map(s -> "$" + prev + s);
}
return Stream.empty();
}
@Override
public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
if (!input.startsWith("$")) {
@ -50,10 +70,8 @@ public class BiomeMaskParser extends InputParser<Mask> {
}
Set<BiomeType> biomes = new HashSet<>();
BiomeRegistry biomeRegistry = worldEdit.getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getBiomeRegistry();
Collection<BiomeType> knownBiomes = BiomeType.REGISTRY.values();
for (String biomeName : Splitter.on(",").split(input.substring(1))) {
BiomeType biome = Biomes.findBiomeByName(knownBiomes, biomeName, biomeRegistry);
BiomeType biome = BiomeType.REGISTRY.get(biomeName);
if (biome == null) {
throw new InputParseException("Unknown biome '" + biomeName + '\'');
}

View File

@ -20,6 +20,7 @@
package com.sk89q.worldedit.extension.factory.parser.mask;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.util.SuggestionHelper;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.function.mask.BlockCategoryMask;
@ -28,12 +29,20 @@ import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.session.request.RequestExtent;
import com.sk89q.worldedit.world.block.BlockCategory;
import java.util.Locale;
import java.util.stream.Stream;
public class BlockCategoryMaskParser extends InputParser<Mask> {
public BlockCategoryMaskParser(WorldEdit worldEdit) {
super(worldEdit);
}
@Override
public Stream<String> getSuggestions(String input) {
return SuggestionHelper.getBlockCategorySuggestions(input, false);
}
@Override
public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
if (!input.startsWith("##")) {
@ -41,7 +50,7 @@ public class BlockCategoryMaskParser extends InputParser<Mask> {
}
// This means it's a tag mask.
BlockCategory category = BlockCategory.REGISTRY.get(input.substring(2).toLowerCase());
BlockCategory category = BlockCategory.REGISTRY.get(input.substring(2).toLowerCase(Locale.ROOT));
if (category == null) {
throw new InputParseException("Unrecognised tag '" + input.substring(2) + '\'');
} else {

View File

@ -28,12 +28,22 @@ import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.session.request.RequestExtent;
import java.util.stream.Stream;
public class BlockStateMaskParser extends InputParser<Mask> {
public BlockStateMaskParser(WorldEdit worldEdit) {
super(worldEdit);
}
@Override
public Stream<String> getSuggestions(String input) {
if (input.isEmpty()) {
return Stream.of("^[", "^=[");
}
return Stream.of("^[", "^=[").filter(s -> s.startsWith(input)); // no block type, can't suggest states
}
@Override
public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
if (!(input.startsWith("^[") || input.startsWith("^=[")) || !input.endsWith("]")) {

View File

@ -30,6 +30,7 @@ import com.sk89q.worldedit.session.request.RequestExtent;
import com.sk89q.worldedit.world.block.BaseBlock;
import java.util.Set;
import java.util.stream.Stream;
/**
* Parses mask input strings.
@ -40,6 +41,11 @@ public class BlocksMaskParser extends InputParser<Mask> {
super(worldEdit);
}
@Override
public Stream<String> getSuggestions(String input) {
return worldEdit.getBlockFactory().getSuggestions(input).stream();
}
@Override
public Mask parseFromInput(String component, ParserContext context) throws InputParseException {
ParserContext tempContext = new ParserContext(context);

View File

@ -19,7 +19,7 @@
package com.sk89q.worldedit.extension.factory.parser.mask;
import com.google.common.collect.Lists;
import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
@ -31,13 +31,15 @@ import java.util.List;
public class ExistingMaskParser extends SimpleInputParser<Mask> {
private final List<String> aliases = ImmutableList.of("#existing");
public ExistingMaskParser(WorldEdit worldEdit) {
super(worldEdit);
}
@Override
public List<String> getMatchedAliases() {
return Lists.newArrayList("#existing");
return aliases;
}
@Override

View File

@ -30,10 +30,10 @@ import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment;
import com.sk89q.worldedit.session.SessionOwner;
import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.session.request.RequestExtent;
import java.util.function.IntSupplier;
import java.util.stream.Stream;
public class ExpressionMaskParser extends InputParser<Mask> {
@ -41,6 +41,14 @@ public class ExpressionMaskParser extends InputParser<Mask> {
super(worldEdit);
}
@Override
public Stream<String> getSuggestions(String input) {
if (input.isEmpty()) {
return Stream.of("=");
}
return Stream.empty();
}
@Override
public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
if (!input.startsWith("=")) {
@ -52,10 +60,10 @@ public class ExpressionMaskParser extends InputParser<Mask> {
WorldEditExpressionEnvironment env = new WorldEditExpressionEnvironment(
new RequestExtent(), Vector3.ONE, Vector3.ZERO);
exp.setEnvironment(env);
if (context.getActor() instanceof SessionOwner) {
SessionOwner owner = (SessionOwner) context.getActor();
if (context.getActor() != null) {
SessionOwner owner = context.getActor();
IntSupplier timeout = () -> WorldEdit.getInstance().getSessionManager().get(owner).getTimeout();
// TODO timeout
return new ExpressionMask(exp, timeout);
}
return new ExpressionMask(exp);
} catch (ExpressionException e) {

View File

@ -19,9 +19,8 @@
package com.sk89q.worldedit.extension.factory.parser.mask;
import com.google.common.collect.Lists;
import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.RegionMask;
@ -32,17 +31,19 @@ import java.util.List;
public class LazyRegionMaskParser extends SimpleInputParser<Mask> {
private final List<String> aliases = ImmutableList.of("#dregion", "#dselection", "#dsel");
public LazyRegionMaskParser(WorldEdit worldEdit) {
super(worldEdit);
}
@Override
public List<String> getMatchedAliases() {
return Lists.newArrayList("#dregion", "#dselection", "#dsel");
return aliases;
}
@Override
public Mask parseFromSimpleInput(String input, ParserContext context) throws InputParseException {
public Mask parseFromSimpleInput(String input, ParserContext context) {
return new RegionMask(new RequestSelection());
}
}

View File

@ -26,12 +26,25 @@ import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.internal.registry.InputParser;
import java.util.stream.Stream;
public class NegateMaskParser extends InputParser<Mask> {
public NegateMaskParser(WorldEdit worldEdit) {
super(worldEdit);
}
@Override
public Stream<String> getSuggestions(String input) {
if (input.isEmpty()) {
return Stream.of("!");
}
if (input.charAt(0) != '!') {
return Stream.empty();
}
return worldEdit.getMaskFactory().getSuggestions(input.substring(1)).stream().map(s -> "!" + s);
}
@Override
public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
if (!input.startsWith("!")) {

View File

@ -20,13 +20,14 @@
package com.sk89q.worldedit.extension.factory.parser.mask;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.NoiseFilter;
import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.math.noise.RandomNoise;
import java.util.stream.Stream;
public class NoiseMaskParser extends InputParser<Mask> {
public NoiseMaskParser(WorldEdit worldEdit) {
@ -34,7 +35,18 @@ public class NoiseMaskParser extends InputParser<Mask> {
}
@Override
public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
public Stream<String> getSuggestions(String input) {
if (input.isEmpty()) {
return Stream.of("%");
}
if (input.charAt(0) != '%') {
return Stream.empty();
}
return Stream.of("%10", "%25", "%50", "%75").filter(s -> s.startsWith(input));
}
@Override
public Mask parseFromInput(String input, ParserContext context) {
if (!input.startsWith("%")) {
return null;
}

View File

@ -31,12 +31,26 @@ import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.session.request.RequestExtent;
import java.util.stream.Stream;
public class OffsetMaskParser extends InputParser<Mask> {
public OffsetMaskParser(WorldEdit worldEdit) {
super(worldEdit);
}
@Override
public Stream<String> getSuggestions(String input) {
if (input.isEmpty()) {
return Stream.of(">", "<");
}
final char firstChar = input.charAt(0);
if (firstChar != '>' && firstChar != '<') {
return Stream.empty();
}
return worldEdit.getMaskFactory().getSuggestions(input.substring(1)).stream().map(s -> firstChar + s);
}
@Override
public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
final char firstChar = input.charAt(0);

View File

@ -19,7 +19,7 @@
package com.sk89q.worldedit.extension.factory.parser.mask;
import com.google.common.collect.Lists;
import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.input.InputParseException;
@ -32,13 +32,15 @@ import java.util.List;
public class RegionMaskParser extends SimpleInputParser<Mask> {
private final List<String> aliases = ImmutableList.of("#region", "#selection", "#sel");
public RegionMaskParser(WorldEdit worldEdit) {
super(worldEdit);
}
@Override
public List<String> getMatchedAliases() {
return Lists.newArrayList("#region", "#selection", "#sel");
return aliases;
}
@Override

View File

@ -19,7 +19,7 @@
package com.sk89q.worldedit.extension.factory.parser.mask;
import com.google.common.collect.Lists;
import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.function.mask.Mask;
@ -31,13 +31,15 @@ import java.util.List;
public class SolidMaskParser extends SimpleInputParser<Mask> {
private final List<String> aliases = ImmutableList.of("#solid");
public SolidMaskParser(WorldEdit worldEdit) {
super(worldEdit);
}
@Override
public List<String> getMatchedAliases() {
return Lists.newArrayList("#solid");
return aliases;
}
@Override

View File

@ -20,18 +20,18 @@
package com.sk89q.worldedit.extension.factory.parser.pattern;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.util.SuggestionHelper;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.function.pattern.BlockPattern;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.function.pattern.RandomPattern;
import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.world.block.BlockCategory;
import com.sk89q.worldedit.world.block.BlockType;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class BlockCategoryPatternParser extends InputParser<Pattern> {
@ -40,8 +40,8 @@ public class BlockCategoryPatternParser extends InputParser<Pattern> {
}
@Override
public List<String> getSuggestions() {
return BlockCategory.REGISTRY.keySet().stream().map(str -> "##" + str).collect(Collectors.toList());
public Stream<String> getSuggestions(String input) {
return SuggestionHelper.getBlockCategorySuggestions(input, true);
}
@Override
@ -49,7 +49,7 @@ public class BlockCategoryPatternParser extends InputParser<Pattern> {
if (!input.startsWith("##")) {
return null;
}
String tag = input.substring(2).toLowerCase();
String tag = input.substring(2).toLowerCase(Locale.ROOT);
boolean anyState = false;
if (tag.startsWith("*")) {
tag = tag.substring(1);
@ -69,10 +69,10 @@ public class BlockCategoryPatternParser extends InputParser<Pattern> {
if (anyState) {
blocks.stream().flatMap(blockType -> blockType.getAllStates().stream()).forEach(state ->
randomPattern.add((state), 1.0));
randomPattern.add(state, 1.0));
} else {
for (BlockType blockType : blocks) {
randomPattern.add((blockType.getDefaultState()), 1.0);
randomPattern.add(blockType.getDefaultState(), 1.0);
}
}

View File

@ -19,7 +19,6 @@
package com.sk89q.worldedit.extension.factory.parser.pattern;
import com.google.common.collect.Lists;
import com.sk89q.worldedit.EmptyClipboardException;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
@ -32,7 +31,8 @@ import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.session.ClipboardHolder;
import java.util.List;
import java.util.Locale;
import java.util.stream.Stream;
public class ClipboardPatternParser extends InputParser<Pattern> {
@ -41,8 +41,28 @@ public class ClipboardPatternParser extends InputParser<Pattern> {
}
@Override
public List<String> getSuggestions() {
return Lists.newArrayList("#clipboard", "#copy");
public Stream<String> getSuggestions(String input) {
if (input.isEmpty()) {
return Stream.of("#clipboard");
}
String[] offsetParts = input.split("@", 2);
String firstLower = offsetParts[0].toLowerCase(Locale.ROOT);
final boolean isClip = "#clipboard".startsWith(firstLower);
final boolean isCopy = "#copy".startsWith(firstLower);
if (isClip || isCopy) {
if (offsetParts.length == 2) {
String coords = offsetParts[1];
if (coords.isEmpty()) {
return Stream.of(input + "[x,y,z]");
}
} else {
if (isClip) {
return Stream.of("#clipboard", "#clipboard@[x,y,z]");
}
return Stream.of("#copy", "#copy@[x,y,z]");
}
}
return Stream.empty();
}
@Override
@ -83,4 +103,5 @@ public class ClipboardPatternParser extends InputParser<Pattern> {
throw new InputParseException("No session is available, so no clipboard is available");
}
}
}

View File

@ -26,10 +26,6 @@ import com.boydti.fawe.object.random.TrueRandom;
import com.boydti.fawe.util.StringMan;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandLocals;
import com.google.common.collect.Lists;
import com.sk89q.worldedit.EmptyClipboardException;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.PatternCommands;
import com.sk89q.worldedit.extension.input.InputParseException;
@ -41,12 +37,10 @@ import com.sk89q.worldedit.function.pattern.RandomPattern;
import com.sk89q.worldedit.internal.command.ActorAuthorizer;
import com.sk89q.worldedit.internal.command.WorldEditBinding;
import com.sk89q.worldedit.internal.expression.Expression;
import com.sk89q.worldedit.internal.expression.ExpressionException;
import com.sk89q.worldedit.util.command.Dispatcher;
import com.sk89q.worldedit.util.command.SimpleDispatcher;
import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
import com.sk89q.worldedit.world.block.BlockTypes;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -115,16 +109,16 @@ public class DefaultPatternParser extends FaweParser<Pattern> {
if (charMask) {
switch (char0) {
case '$': {
String value = command.substring(1) + ((entry.getValue().isEmpty()) ? "" : "[" + StringMan.join(entry.getValue(), "][") + "]");
if (value.contains(":")) {
if (value.charAt(0) == ':') value.replaceFirst(":", "");
value = value.replaceAll(":", "][");
if (char0 == '$') {
String value = command.substring(1) + ((entry.getValue().isEmpty()) ? ""
: "[" + StringMan.join(entry.getValue(), "][") + "]");
if (value.contains(":")) {
if (value.charAt(0) == ':') {
value.replaceFirst(":", "");
}
pattern = parseFromInput(char0 + "[" + value + "]", context);
break;
value = value.replaceAll(":", "][");
}
pattern = parseFromInput(char0 + "[" + value + "]", context);
}
}
if (pattern == null) {
@ -194,14 +188,14 @@ public class DefaultPatternParser extends FaweParser<Pattern> {
}
if (patterns.isEmpty()) {
return null;
} else if (patterns.size() == 1) {
}
if (patterns.size() == 1) {
return patterns.get(0);
} else {
RandomPattern random = new RandomPattern(new TrueRandom());
for (int i = 0; i < patterns.size(); i++) {
random.add(patterns.get(i), chances.get(i));
}
return random;
}
}
RandomPattern random = new RandomPattern(new TrueRandom());
for (int i = 0; i < patterns.size(); i++) {
random.add(patterns.get(i), chances.get(i));
}
return random;
}
}

View File

@ -21,7 +21,6 @@ package com.sk89q.worldedit.extension.factory.parser.pattern;
import com.sk89q.util.StringUtil;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.factory.BlockFactory;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.function.pattern.Pattern;
@ -29,6 +28,7 @@ import com.sk89q.worldedit.function.pattern.RandomPattern;
import com.sk89q.worldedit.internal.registry.InputParser;
import java.util.List;
import java.util.stream.Stream;
public class RandomPatternParser extends InputParser<Pattern> {
@ -36,12 +36,35 @@ public class RandomPatternParser extends InputParser<Pattern> {
super(worldEdit);
}
@Override
public Stream<String> getSuggestions(String input) {
String[] splits = input.split(",", -1);
List<String> patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true);
if (patterns.size() == 1) {
return Stream.empty();
}
// get suggestions for the last token only
String token = patterns.get(patterns.size() - 1);
String previous = String.join(",", patterns.subList(0, patterns.size() - 1));
if (token.matches("[0-9]+(\\.[0-9]*)?%.*")) {
String[] p = token.split("%");
if (p.length < 2) {
return Stream.empty();
} else {
token = p[1];
}
}
final List<String> innerSuggestions = worldEdit.getPatternFactory().getSuggestions(token);
return innerSuggestions.stream().map(s -> previous + "," + s);
}
@Override
public Pattern parseFromInput(String input, ParserContext context) throws InputParseException {
RandomPattern randomPattern = new RandomPattern();
String[] splits = input.split(",");
List<String> patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']');
String[] splits = input.split(",", -1);
List<String> patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true);
if (patterns.size() == 1) {
return null; // let a 'single'-pattern parser handle it
}

View File

@ -22,16 +22,31 @@ package com.sk89q.worldedit.extension.factory.parser.pattern;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.function.pattern.BlockPattern;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.function.pattern.RandomStatePattern;
import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.FuzzyBlockState;
import java.util.stream.Stream;
public class RandomStatePatternParser extends InputParser<Pattern> {
public RandomStatePatternParser(WorldEdit worldEdit) {
super(worldEdit);
}
@Override
public Stream<String> getSuggestions(String input) {
if (input.isEmpty()) {
return Stream.of("*");
}
if (!input.startsWith("*")) {
return Stream.empty();
}
return worldEdit.getBlockFactory().getSuggestions(input.substring(1)).stream().map(s -> "*" + s);
}
@Override
public Pattern parseFromInput(String input, ParserContext context) throws InputParseException {
if (!input.startsWith("*")) {
@ -44,9 +59,11 @@ public class RandomStatePatternParser extends InputParser<Pattern> {
context.setPreferringWildcard(wasFuzzy);
if (block.getStates().size() == block.getBlockType().getPropertyMap().size()) {
// they requested random with *, but didn't leave any states empty - simplify
return (block);
return block;
} else if (block.toImmutableState() instanceof FuzzyBlockState) {
return new RandomStatePattern((FuzzyBlockState) block.toImmutableState());
} else {
return null; // only should happen if parseLogic changes
}
}
}
}

View File

@ -22,19 +22,25 @@ package com.sk89q.worldedit.extension.factory.parser.pattern;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.function.pattern.BlockPattern;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.internal.registry.InputParser;
import java.util.stream.Stream;
public class SingleBlockPatternParser extends InputParser<Pattern> {
public SingleBlockPatternParser(WorldEdit worldEdit) {
super(worldEdit);
}
@Override
public Stream<String> getSuggestions(String input) {
return worldEdit.getBlockFactory().getSuggestions(input).stream();
}
@Override
public Pattern parseFromInput(String input, ParserContext context) throws InputParseException {
return (worldEdit.getBlockFactory().parseFromInput(input, context));
return worldEdit.getBlockFactory().parseFromInput(input, context);
}
}

View File

@ -19,10 +19,8 @@
package com.sk89q.worldedit.extension.factory.parser.pattern;
import com.google.common.base.Splitter;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.util.SuggestionHelper;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.extent.Extent;
@ -32,10 +30,9 @@ import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.function.pattern.StateApplyingPattern;
import com.sk89q.worldedit.function.pattern.TypeApplyingPattern;
import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.world.block.BlockState;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
public class TypeOrStateApplyingPatternParser extends InputParser<Pattern> {
@ -44,6 +41,30 @@ public class TypeOrStateApplyingPatternParser extends InputParser<Pattern> {
super(worldEdit);
}
@Override
public Stream<String> getSuggestions(String input) {
if (input.isEmpty()) {
return Stream.of("^");
}
if (!input.startsWith("^")) {
return Stream.empty();
}
input = input.substring(1);
String[] parts = input.split("\\[", 2);
String type = parts[0];
if (parts.length == 1) {
return worldEdit.getBlockFactory().getSuggestions(input).stream().map(s -> "^" + s);
} else {
if (type.isEmpty()) {
return Stream.empty(); // without knowing a type, we can't really suggest states
} else {
return SuggestionHelper.getBlockPropertySuggestions(type, parts[1]).map(s -> "^" + s);
}
}
}
@Override
public Pattern parseFromInput(String input, ParserContext context) throws InputParseException {
if (!input.startsWith("^")) {
@ -60,10 +81,21 @@ public class TypeOrStateApplyingPatternParser extends InputParser<Pattern> {
worldEdit.getBlockFactory().parseFromInput(type, context).getBlockType().getDefaultState());
} else {
// states given
if (!parts[1].endsWith("]")) throw new InputParseException("Invalid state format.");
Map<String, String> statesToSet = Splitter.on(',')
.omitEmptyStrings().trimResults().withKeyValueSeparator('=')
.split(parts[1].substring(0, parts[1].length() - 1));
if (!parts[1].endsWith("]")) throw new InputParseException("State is missing trailing ']'");
final String[] states = parts[1].substring(0, parts[1].length() - 1).split(",");
Map<String, String> statesToSet = new HashMap<>();
for (String state : states) {
if (state.isEmpty()) throw new InputParseException("Empty part in state");
String[] propVal = state.split("=", 2);
if (propVal.length != 2) throw new InputParseException("Missing '=' separator");
final String prop = propVal[0];
if (prop.isEmpty()) throw new InputParseException("Empty property in state");
final String value = propVal[1];
if (value.isEmpty()) throw new InputParseException("Empty value in state");
if (statesToSet.put(prop, value) != null) {
throw new InputParseException("Duplicate properties in state");
}
}
if (type.isEmpty()) {
return new StateApplyingPattern(extent, statesToSet);
} else {

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.extension.platform;
import com.sk89q.worldedit.world.DataFixer;
import com.sk89q.worldedit.world.World;
import java.util.Collections;
@ -39,4 +40,8 @@ public abstract class AbstractPlatform implements Platform {
return Collections.emptyList();
}
@Override
public DataFixer getDataFixer() {
return null;
}
}

View File

@ -23,6 +23,7 @@ import com.sk89q.worldedit.NotABlockException;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.internal.cui.CUIEvent;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.MutableBlockVector3;
@ -44,6 +45,7 @@ import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.item.ItemTypes;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import javax.annotation.Nullable;
import java.io.File;
/**
@ -107,7 +109,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
byte free = 0;
BlockVector3 mutablePos = MutableBlockVector3.at(0, 0, 0);
BlockVector3 mutablePos = MutableBlockVector3.ZERO;
while (y <= world.getMaximumPoint().getBlockY() + 2) {
if (!world.getBlock(mutablePos.setComponents(x, y, z)).getBlockType().getMaterial().isMovementBlocker()) {
++free;
@ -118,7 +120,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
if (free == 2) {
final BlockVector3 pos = mutablePos.setComponents(x, y - 2, z);
final BlockStateHolder state = world.getBlock(pos);
setPosition(new Location(world, Vector3.at(x + 0.5, y - 2 + BlockTypeUtil.centralTopLimit(state), z + 0.5)));
setPosition(Vector3.at(x + 0.5, y - 2 + BlockTypeUtil.centralTopLimit(state), z + 0.5));
return;
}
@ -137,7 +139,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
final BlockVector3 pos = BlockVector3.at(x, y, z);
final BlockState id = world.getBlock(pos);
if (id.getBlockType().getMaterial().isMovementBlocker()) {
setPosition(new Location(world, Vector3.at(x + 0.5, y + + BlockTypeUtil.centralTopLimit(id), z + 0.5)));
setPosition(Vector3.at(x + 0.5, y + + BlockTypeUtil.centralTopLimit(id), z + 0.5));
return;
}
@ -161,7 +163,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
int maxY = world.getMaxY();
if (y >= maxY) return false;
BlockMaterial initialMaterial = world.getBlockType(BlockVector3.at(x, y, z)).getMaterial();
BlockMaterial initialMaterial = world.getBlock(BlockVector3.at(x, y, z)).getMaterial();
boolean lastState = initialMaterial.isMovementBlocker() && initialMaterial.isFullCube();
@ -199,6 +201,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
lastState = true;
}
}
return false;
}
@ -206,20 +209,19 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
public boolean descendLevel() {
final Location pos = getBlockIn();
final int x = pos.getBlockX();
int y = Math.max(0, pos.getBlockY());
int y = Math.max(0, pos.getBlockY() - 1);
final int z = pos.getBlockZ();
final Extent world = pos.getExtent();
BlockMaterial initialMaterial = world.getBlockType(BlockVector3.at(x, y, z)).getMaterial();
BlockMaterial initialMaterial = world.getBlock(BlockVector3.at(x, y, z)).getMaterial();
boolean lastState = initialMaterial.isMovementBlocker() && initialMaterial.isFullCube();
double height = 1.85;
double freeEnd = -1;
int maxY = world.getMaxY();
if (y <= 2) return false;
double freeEnd = -1;
double height = 1.85;
for (int level = y + 1; level > 0; level--) {
BlockState state;
if (level >= maxY) state = BlockTypes.VOID_AIR.getDefaultState();
@ -252,6 +254,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
freeEnd = -1;
}
}
return false;
}
@ -344,13 +347,29 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
@Override
public Location getBlockTrace(int range, boolean useLastBlock) {
TargetBlock tb = new TargetBlock(this, range, 0.2);
return (useLastBlock ? tb.getAnyTargetBlock() : tb.getTargetBlock());
return getBlockTrace(range, useLastBlock, null);
}
@Override
public Location getBlockTraceFace(int range, boolean useLastBlock) {
return getBlockTraceFace(range, useLastBlock, null);
}
@Override
public Location getBlockTrace(int range, boolean useLastBlock, @Nullable Mask stopMask) {
TargetBlock tb = new TargetBlock(this, range, 0.2);
if (stopMask != null) {
tb.setStopMask(stopMask);
}
return (useLastBlock ? tb.getAnyTargetBlock() : tb.getTargetBlock());
}
@Override
public Location getBlockTraceFace(int range, boolean useLastBlock, @Nullable Mask stopMask) {
TargetBlock tb = new TargetBlock(this, range, 0.2);
if (stopMask != null) {
tb.setStopMask(stopMask);
}
return (useLastBlock ? tb.getAnyTargetBlockFace() : tb.getTargetBlockFace());
}
@ -393,7 +412,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
if (typeId.hasBlockType()) {
return typeId.getBlockType().getDefaultState().toBaseBlock();
} else {
return BlockTypes.AIR.getDefaultState().toBaseBlock();
throw new NotABlockException();
}
}

View File

@ -23,6 +23,7 @@ import com.sk89q.worldedit.internal.cui.CUIEvent;
import com.sk89q.worldedit.session.SessionOwner;
import com.sk89q.worldedit.util.Identifiable;
import com.sk89q.worldedit.util.auth.Subject;
import com.sk89q.worldedit.util.formatting.text.Component;
import java.io.File;
@ -75,6 +76,13 @@ public interface Actor extends Identifiable, SessionOwner, Subject {
*/
void printError(String msg);
/**
* Print a {@link Component}.
*
* @param component The component to print
*/
void print(Component component);
/**
* Returns true if the actor can destroy bedrock.
*

View File

@ -50,12 +50,12 @@ public enum Capability {
USER_COMMANDS {
@Override
void initialize(PlatformManager platformManager, Platform platform) {
platformManager.getCommandManager().register(platform);
platformManager.getPlatformCommandManager().registerCommandsWith(platform);
}
@Override
void unload(PlatformManager platformManager, Platform platform) {
platformManager.getCommandManager().unregister();
platformManager.getPlatformCommandManager().removeCommands();
}
},

View File

@ -1,574 +0,0 @@
/*
* 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/>.
*/
package com.sk89q.worldedit.extension.platform;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.command.AnvilCommands;
import com.boydti.fawe.command.CFICommand;
import com.boydti.fawe.command.MaskBinding;
import com.boydti.fawe.command.PatternBinding;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.object.task.ThrowableSupplier;
import com.boydti.fawe.util.StringMan;
import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.util.chat.UsageMessage;
import com.boydti.fawe.wrappers.LocationMaskedPlayerWrapper;
import com.google.common.base.Joiner;
import com.sk89q.minecraft.util.commands.*;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.*;
import com.sk89q.worldedit.command.argument.ReplaceParser;
import com.sk89q.worldedit.command.argument.TreeGeneratorParser;
import com.sk89q.worldedit.command.composition.ApplyCommand;
import com.sk89q.worldedit.command.composition.DeformCommand;
import com.sk89q.worldedit.command.composition.PaintCommand;
import com.sk89q.worldedit.command.composition.ShapedBrushCommand;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.event.platform.CommandEvent;
import com.sk89q.worldedit.event.platform.CommandSuggestionEvent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.factory.Deform;
import com.sk89q.worldedit.function.factory.Deform.Mode;
import com.sk89q.worldedit.internal.command.*;
import com.sk89q.worldedit.scripting.CommandScriptLoader;
import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.util.auth.AuthorizationException;
import com.sk89q.worldedit.util.command.*;
import com.sk89q.worldedit.util.command.composition.ProvidedValue;
import com.sk89q.worldedit.util.command.fluent.CommandGraph;
import com.sk89q.worldedit.util.command.fluent.DispatcherNode;
import com.sk89q.worldedit.util.command.parametric.AParametricCallable;
import com.sk89q.worldedit.util.command.parametric.ExceptionConverter;
import com.sk89q.worldedit.util.command.parametric.LegacyCommandsHandler;
import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
import com.sk89q.worldedit.util.eventbus.Subscribe;
import com.sk89q.worldedit.util.logging.DynamicStreamHandler;
import com.sk89q.worldedit.util.logging.LogFormat;
import com.sk89q.worldedit.world.World;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.regex.Pattern;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.sk89q.worldedit.util.command.composition.LegacyCommandAdapter.adapt;
/**
* Handles the registration and invocation of commands.
*
* <p>This class is primarily for internal usage.</p>
*/
public final class CommandManager {
public static final Pattern COMMAND_CLEAN_PATTERN = Pattern.compile("^[/]+");
private static final Logger log = LoggerFactory.getLogger(CommandManager.class);
private static final java.util.logging.Logger commandLog =
java.util.logging.Logger.getLogger(CommandManager.class.getCanonicalName() + ".CommandLog");
private static final Pattern numberFormatExceptionPattern = Pattern.compile("^For input string: \"(.*)\"$");
private final WorldEdit worldEdit;
private final PlatformManager platformManager;
private volatile Dispatcher dispatcher;
private volatile Platform platform;
private final DynamicStreamHandler dynamicHandler = new DynamicStreamHandler();
private final ExceptionConverter exceptionConverter;
private ParametricBuilder builder;
private Map<Object, String[]> methodMap;
private Map<CommandCallable, String[][]> commandMap;
private static CommandManager INSTANCE;
/**
* Create a new instance.
*
* @param worldEdit the WorldEdit instance
*/
public CommandManager(final WorldEdit worldEdit, PlatformManager platformManager) {
checkNotNull(worldEdit);
checkNotNull(platformManager);
INSTANCE = this;
this.worldEdit = worldEdit;
this.platformManager = platformManager;
this.exceptionConverter = new WorldEditExceptionConverter(worldEdit);
// Register this instance for command events
worldEdit.getEventBus().register(this);
// Setup the logger
commandLog.addHandler(dynamicHandler);
dynamicHandler.setFormatter(new LogFormat());
// Set up the commands manager
builder = new ParametricBuilder();
builder.setAuthorizer(new ActorAuthorizer());
builder.setDefaultCompleter(new UserCommandCompleter(platformManager));
builder.addBinding(new WorldEditBinding(worldEdit));
builder.addBinding(new PatternBinding(worldEdit), com.sk89q.worldedit.function.pattern.Pattern.class);
builder.addBinding(new MaskBinding(worldEdit), com.sk89q.worldedit.function.mask.Mask.class);
builder.addInvokeListener(new LegacyCommandsHandler());
builder.addInvokeListener(new CommandLoggingHandler(worldEdit, commandLog));
this.methodMap = new ConcurrentHashMap<>();
this.commandMap = new ConcurrentHashMap<>();
}
/**
* Register all the methods in the class as commands<br>
* - You should try to register commands during startup
*
* @param clazz The class containing all the commands
*/
public void registerCommands(Object clazz) {
registerCommands(clazz, new String[0]);
}
/**
* Create a command with the provided aliases and register all methods of the class as sub commands.<br>
* - You should try to register commands during startup
*
* @param clazz The class containing all the sub command methods
* @param aliases The aliases to give the command
*/
public synchronized void registerCommands(Object clazz, String... aliases) {
if (dispatcher != null) {
if (aliases.length == 0) {
builder.registerMethodsAsCommands(dispatcher, clazz);
} else {
DispatcherNode graph = new CommandGraph().builder(builder).commands();
graph = graph.registerMethods(clazz);
dispatcher.registerCommand(graph.graph().getDispatcher(), aliases);
}
platform.registerCommands(dispatcher);
} else {
methodMap.put(clazz, aliases);
}
}
/**
* Create a command with the provided aliases and register all methods of the class as sub commands.<br>
* - You should try to register commands during startup
*
* @param clazz The class containing all the sub command methods
* @param aliases The aliases to give the command
*/
public synchronized void registerCommands(Object clazz, CallableProcessor processor, String... aliases) {
if (dispatcher != null) {
if (aliases.length == 0) {
builder.registerMethodsAsCommands(dispatcher, clazz, processor);
} else {
DispatcherNode graph = new CommandGraph().builder(builder).commands();
graph = graph.registerMethods(clazz, processor);
dispatcher.registerCommand(graph.graph().getDispatcher(), aliases);
}
platform.registerCommands(dispatcher);
} else {
methodMap.put(clazz, aliases);
}
}
public synchronized void registerCommand(String[] aliases, Command command, CommandCallable callable) {
if (dispatcher != null) {
if (aliases.length == 0) {
dispatcher.registerCommand(callable, command.aliases());
} else {
DispatcherNode graph = new CommandGraph().builder(builder).commands();
graph = graph.register(callable, command.aliases());
dispatcher.registerCommand(graph.graph().getDispatcher(), aliases);
}
platform.registerCommands(dispatcher);
} else {
commandMap.putIfAbsent(callable, new String[][] {aliases, command.aliases()});
}
}
public ParametricBuilder getBuilder() {
return builder;
}
/**
* Initialize the dispatcher
*/
public synchronized void setupDispatcher() {
if (Settings.IMP.ENABLED_COMPONENTS.COMMANDS) {
DispatcherNode graph = new CommandGraph().builder(builder).commands();
for (Map.Entry<Object, String[]> entry : methodMap.entrySet()) {
// add command
String[] aliases = entry.getValue();
if (aliases.length == 0) {
graph = graph.registerMethods(entry.getKey());
} else {
graph = graph.group(aliases).registerMethods(entry.getKey()).parent();
}
}
for (Map.Entry<CommandCallable, String[][]> entry : commandMap.entrySet()) {
String[][] aliases = entry.getValue();
CommandCallable callable = entry.getKey();
if (aliases[0].length == 0) {
graph = graph.register(callable, aliases[1]);
} else {
graph = graph.group(aliases[0]).register(callable, aliases[1]).parent();
}
}
commandMap.clear();
methodMap.clear();
dispatcher = graph
.group("/anvil")
.describeAs("Anvil command")
.registerMethods(new AnvilCommands(worldEdit)).parent()
.registerMethods(new CFICommand(worldEdit, builder))
.registerMethods(new BiomeCommands(worldEdit))
.registerMethods(new ChunkCommands(worldEdit))
.registerMethods(new ClipboardCommands(worldEdit))
.registerMethods(new OptionsCommands(worldEdit))
.registerMethods(new GenerationCommands(worldEdit))
.registerMethods(new HistoryCommands(worldEdit))
.registerMethods(new NavigationCommands(worldEdit))
.registerMethods(new RegionCommands(worldEdit))
.registerMethods(new ScriptingCommands(worldEdit))
.registerMethods(new SelectionCommands(worldEdit))
.registerMethods(new SnapshotUtilCommands(worldEdit))
.registerMethods(new BrushOptionsCommands(worldEdit))
.registerMethods(new ToolCommands(worldEdit))
.registerMethods(new UtilityCommands(worldEdit))
.registerSubMethods(new WorldEditCommands(worldEdit))
.registerSubMethods(new SchematicCommands(worldEdit))
.registerSubMethods(new SnapshotCommands(worldEdit))
.groupAndDescribe(BrushCommands.class)
.registerMethods(new BrushCommands(worldEdit))
.registerMethods(new ToolCommands(worldEdit))
.registerMethods(new BrushOptionsCommands(worldEdit))
.register(adapt(new ShapedBrushCommand(new DeformCommand(), "worldedit.brush.deform")), "deform")
.register(adapt(new ShapedBrushCommand(new ApplyCommand(new ReplaceParser(), "Set all blocks within region"), "worldedit.brush.set")), "set")
.register(adapt(new ShapedBrushCommand(new PaintCommand(), "worldedit.brush.paint")), "paint")
.register(adapt(new ShapedBrushCommand(new ApplyCommand(), "worldedit.brush.apply")), "apply")
.register(adapt(new ShapedBrushCommand(new PaintCommand(new TreeGeneratorParser("treeType")), "worldedit.brush.forest")), "forest")
.register(adapt(new ShapedBrushCommand(ProvidedValue.create(new Deform("y-=1", Mode.RAW_COORD), "Raise one block"), "worldedit.brush.raise")), "raise")
.register(adapt(new ShapedBrushCommand(ProvidedValue.create(new Deform("y+=1", Mode.RAW_COORD), "Lower one block"), "worldedit.brush.lower")), "lower")
.parent()
.group("superpickaxe", "pickaxe", "sp").describeAs("Super-pickaxe commands")
.registerMethods(new SuperPickaxeCommands(worldEdit))
.parent().graph().getDispatcher();
if (platform != null) {
platform.registerCommands(dispatcher);
}
}
}
public static CommandManager getInstance() {
return INSTANCE;
}
public ExceptionConverter getExceptionConverter() {
return exceptionConverter;
}
public void register(Platform platform) {
log.info("Registering commands with " + platform.getClass().getCanonicalName());
this.platform = platform;
// Delay command registration to allow time for other plugins to hook into FAWE
try {
new CommandScriptLoader().load();
} catch (Throwable e) {
e.printStackTrace();
}
LocalConfiguration config = platform.getConfiguration();
boolean logging = config.logCommands;
String path = config.logFile;
// Register log
if (!logging || path.isEmpty()) {
dynamicHandler.setHandler(null);
commandLog.setLevel(Level.OFF);
} else {
File file = new File(config.getWorkingDirectory(), path);
commandLog.setLevel(Level.ALL);
log.info("Logging WorldEdit commands to " + file.getAbsolutePath());
try {
dynamicHandler.setHandler(new FileHandler(file.getAbsolutePath(), true));
} catch (IOException e) {
log.warn("Could not use command log file " + path + ": " + e.getMessage());
}
}
setupDispatcher();
}
void unregister() {
dynamicHandler.setHandler(null);
}
public String[] commandDetection(String[] split) {
// Quick script shortcut
if (split[0].matches("^[^/].*\\.js$")) {
String[] newSplit = new String[split.length + 1];
System.arraycopy(split, 0, newSplit, 1, split.length);
newSplit[0] = "cs";
newSplit[1] = newSplit[1];
split = newSplit;
}
String searchCmd = split[0].toLowerCase();
// Try to detect the command
if (!dispatcher.contains(searchCmd)) {
if (worldEdit.getConfiguration().noDoubleSlash && dispatcher.contains("/" + searchCmd)) {
split[0] = "/" + split[0];
} else if (searchCmd.length() >= 2 && searchCmd.charAt(0) == '/' && dispatcher.contains(searchCmd.substring(1))) {
split[0] = split[0].substring(1);
}
}
return split;
}
public void handleCommandOnCurrentThread(CommandEvent event) {
Actor actor = platformManager.createProxyActor(event.getActor());
String[] split = commandDetection(event.getArguments().split(" "));
// No command found!
if (!dispatcher.contains(split[0])) {
return;
}
LocalSession session = worldEdit.getSessionManager().get(actor);
Request.request().setSession(session);
if (actor instanceof Entity) {
Extent extent = ((Entity) actor).getExtent();
if (extent instanceof World) {
Request.request().setWorld(((World) extent));
}
}
LocalConfiguration config = worldEdit.getConfiguration();
CommandLocals locals = new CommandLocals();
final FawePlayer fp = FawePlayer.wrap(actor);
if (fp == null) {
throw new IllegalArgumentException("FAWE doesn't support: " + actor);
}
final Set<String> failedPermissions = new LinkedHashSet<>();
locals.put("failed_permissions", failedPermissions);
locals.put(LocalSession.class, session);
if (actor instanceof Player) {
Player player = (Player) actor;
Player unwrapped = LocationMaskedPlayerWrapper.unwrap(player);
actor = new LocationMaskedPlayerWrapper(unwrapped, player.getLocation(), true) {
@Override
public boolean hasPermission(String permission) {
if (!super.hasPermission(permission)) {
failedPermissions.add(permission);
return false;
}
return true;
}
@Override
public void checkPermission(String permission) throws AuthorizationException {
try {
super.checkPermission(permission);
} catch (AuthorizationException e) {
failedPermissions.add(permission);
throw e;
}
}
};
}
locals.put(Actor.class, actor);
locals.put("arguments", event.getArguments());
ThrowableSupplier<Throwable> task =
() -> dispatcher.call(Joiner.on(" ").join(split), locals, new String[0]);
handleCommandTask(task, locals, actor, session, failedPermissions, fp);
}
public Object handleCommandTask(ThrowableSupplier<Throwable> task, CommandLocals locals) {
return handleCommandTask(task, locals, null, null, null, null);
}
private Object handleCommandTask(ThrowableSupplier<Throwable> task, CommandLocals locals, @Nullable
Actor actor, @Nullable LocalSession session, @Nullable Set<String> failedPermissions, @Nullable FawePlayer fp) {
Request.reset();
if (actor == null) actor = locals.get(Actor.class);
if (session == null) session = locals.get(LocalSession.class);
long start = System.currentTimeMillis();
try {
// This is a bit of a hack, since the call method can only throw CommandExceptions
// everything needs to be wrapped at least once. Which means to handle all WorldEdit
// exceptions without writing a hook into every dispatcher, we need to unwrap these
// exceptions and rethrow their converted form, if their is one.
try {
Request.request().setActor(actor);
return task.get();
} catch (Throwable t) {
// Use the exception converter to convert the exception if any of its causes
// can be converted, otherwise throw the original exception
Throwable next = t;
exceptionConverter.convert(next);
while (next.getCause() != null) {
next = next.getCause();
exceptionConverter.convert(next);
}
throw next;
}
} catch (CommandPermissionsException e) {
if (failedPermissions == null) failedPermissions = (Set<String>) locals.get("failed_permissions");
if (failedPermissions != null) BBC.NO_PERM.send(actor, StringMan.join(failedPermissions, " "));
} catch (InvalidUsageException e) {
if (e.isFullHelpSuggested()) {
CommandCallable cmd = e.getCommand();
if (cmd instanceof Dispatcher) {
try {
String args = locals.get("arguments") + "";
CommandContext context = new CommandContext(("ignoreThis " + args).split(" "), new HashSet<>(), false, locals);
UtilityCommands.help(context, worldEdit, actor);
} catch (CommandException e1) {
e1.printStackTrace();
}
} else {
if (fp == null) fp = FawePlayer.wrap(actor);
new UsageMessage(cmd, e.getCommandUsed((WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/"), ""), locals).send(fp);
}
String message = e.getMessage();
if (message != null) {
actor.printError(message);
}
} else {
String message = e.getMessage();
actor.printRaw((message != null ? message : "The command was not used properly (no more help available)."));
BBC.COMMAND_SYNTAX.send(actor, e.getSimpleUsageString("/"));
}
} catch (CommandException e) {
String message = e.getMessage();
if (message != null) {
actor.printError(e.getMessage());
} else {
actor.printError("An unknown FAWE error has occurred! Please see console.");
log.error("An unknown FAWE error occurred", e);
}
} catch (Throwable e) {
Exception faweException = FaweException.get(e);
if (faweException != null) {
BBC.WORLDEDIT_CANCEL_REASON.send(actor, faweException.getMessage());
} else {
actor.printError("There was an error handling a FAWE command: [See console]");
actor.printRaw(e.getClass().getName() + ": " + e.getMessage());
log.error("An unexpected error occurred while handling a FAWE command", e);
}
} finally {
final EditSession editSession = locals.get(EditSession.class);
if (editSession != null) {
editSession.flushQueue();
worldEdit.flushBlockBag(locals.get(Actor.class), editSession);
session.remember(editSession);
long time = System.currentTimeMillis() - start;
if (time > 1000) {
BBC.ACTION_COMPLETE.send(actor, (time / 1000d));
}
}
Request.reset();
}
return null;
}
@Subscribe
public void handleCommand(CommandEvent event) {
Request.reset();
Actor actor = event.getActor();
if (actor instanceof Player) {
actor = LocationMaskedPlayerWrapper.wrap((Player) actor);
}
String args = event.getArguments();
CommandEvent finalEvent = new CommandEvent(actor, args);
final FawePlayer<Object> fp = FawePlayer.wrap(actor);
TaskManager.IMP.taskNow(() -> {
int space0 = args.indexOf(' ');
String arg0 = space0 == -1 ? args : args.substring(0, space0);
CommandMapping cmd = dispatcher.get(arg0);
if (cmd != null && cmd.getCallable() instanceof AParametricCallable) {
Command info = ((AParametricCallable) cmd.getCallable()).getDefinition();
if (!info.queued()) {
handleCommandOnCurrentThread(finalEvent);
return;
}
}
if (!fp.runAction(new Runnable() {
@Override public void run() {
CommandManager.this.handleCommandOnCurrentThread(finalEvent);
}
}, false, true)) {
BBC.WORLDEDIT_COMMAND_LIMIT.send(fp);
}
finalEvent.setCancelled(true);
}, Fawe.isMainThread());
}
@Subscribe
public void handleCommandSuggestion(CommandSuggestionEvent event) {
try {
CommandLocals locals = new CommandLocals();
locals.put(Actor.class, event.getActor());
locals.put("arguments", event.getArguments());
event.setSuggestions(dispatcher.getSuggestions(event.getArguments(), locals));
} catch (CommandException e) {
event.getActor().printError(e.getMessage());
}
}
/**
* Get the command dispatcher instance.
*
* @return the command dispatcher
*/
public Dispatcher getDispatcher() {
return dispatcher;
}
public static java.util.logging.Logger getLogger() {
return commandLog;
}
}

View File

@ -21,14 +21,14 @@ package com.sk89q.worldedit.extension.platform;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.util.command.Dispatcher;
import com.sk89q.worldedit.world.DataFixer;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.registry.Registries;
import java.util.List;
import java.util.Map;
import org.enginehub.piston.CommandManager;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
/**
* Represents a platform that WorldEdit has been implemented for.
@ -45,11 +45,25 @@ public interface Platform {
*/
Registries getRegistries();
/**
* Gets the Minecraft data version being used by the platform.
*
* @return the data version
*/
int getDataVersion();
/**
* Get a DataFixer capable of upgrading old data.
*
* @return a data fixer, or null if not supported by this platform
*/
DataFixer getDataFixer();
/**
* Checks if a mob type is valid.
*
* @param type The mob type name to check
* @return Whether the name is a valid mod bype
* @return Whether the name is a valid mod type
*/
boolean isValidMobType(String type);
@ -97,11 +111,11 @@ public interface Platform {
@Nullable World matchWorld(World world);
/**
* Register the commands contained within the given command dispatcher.
* Register the commands contained within the given command manager.
*
* @param dispatcher the dispatcher
* @param commandManager the command manager
*/
void registerCommands(Dispatcher dispatcher);
void registerCommands(CommandManager commandManager);
/**
* Register game hooks.

View File

@ -0,0 +1,734 @@
/*
* 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/>.
*/
package com.sk89q.worldedit.extension.platform;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.command.AnvilCommands;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.task.ThrowableSupplier;
import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.wrappers.LocationMaskedPlayerWrapper;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken;
import com.sk89q.minecraft.util.commands.CommandLocals;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.ApplyBrushCommands;
import com.sk89q.worldedit.command.BiomeCommands;
import com.sk89q.worldedit.command.BiomeCommandsRegistration;
import com.sk89q.worldedit.command.BrushCommands;
import com.sk89q.worldedit.command.ChunkCommands;
import com.sk89q.worldedit.command.ClipboardCommands;
import com.sk89q.worldedit.command.GeneralCommands;
import com.sk89q.worldedit.command.GenerationCommands;
import com.sk89q.worldedit.command.HistoryCommands;
import com.sk89q.worldedit.command.NavigationCommands;
import com.sk89q.worldedit.command.RegionCommands;
import com.sk89q.worldedit.command.SchematicCommands;
import com.sk89q.worldedit.command.SchematicCommandsRegistration;
import com.sk89q.worldedit.command.ScriptingCommands;
import com.sk89q.worldedit.command.SelectionCommands;
import com.sk89q.worldedit.command.SnapshotCommands;
import com.sk89q.worldedit.command.SnapshotCommandsRegistration;
import com.sk89q.worldedit.command.SnapshotUtilCommands;
import com.sk89q.worldedit.command.SuperPickaxeCommands;
import com.sk89q.worldedit.command.SuperPickaxeCommandsRegistration;
import com.sk89q.worldedit.command.ToolCommands;
import com.sk89q.worldedit.command.ToolUtilCommands;
import com.sk89q.worldedit.command.UtilityCommands;
import com.sk89q.worldedit.command.WorldEditCommands;
import com.sk89q.worldedit.command.WorldEditCommandsRegistration;
import com.sk89q.worldedit.command.argument.Arguments;
import com.sk89q.worldedit.command.argument.BooleanConverter;
import com.sk89q.worldedit.command.argument.CommaSeparatedValuesConverter;
import com.sk89q.worldedit.command.argument.DirectionConverter;
import com.sk89q.worldedit.command.argument.DirectionVectorConverter;
import com.sk89q.worldedit.command.argument.EntityRemoverConverter;
import com.sk89q.worldedit.command.argument.EnumConverter;
import com.sk89q.worldedit.command.argument.FactoryConverter;
import com.sk89q.worldedit.command.argument.RegionFactoryConverter;
import com.sk89q.worldedit.command.argument.RegistryConverter;
import com.sk89q.worldedit.command.argument.VectorConverter;
import com.sk89q.worldedit.command.argument.ZonedDateTimeConverter;
import com.sk89q.worldedit.command.util.PermissionCondition;
import com.sk89q.worldedit.command.util.SubCommandPermissionCondition;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.event.platform.CommandEvent;
import com.sk89q.worldedit.event.platform.CommandSuggestionEvent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.annotation.Selection;
import com.sk89q.worldedit.internal.command.CommandArgParser;
import com.sk89q.worldedit.internal.command.CommandLoggingHandler;
import com.sk89q.worldedit.internal.command.CommandRegistrationHandler;
import com.sk89q.worldedit.internal.command.exception.ExceptionConverter;
import com.sk89q.worldedit.internal.command.exception.WorldEditExceptionConverter;
import com.sk89q.worldedit.internal.util.Substring;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.scripting.CommandScriptLoader;
import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.util.auth.AuthorizationException;
import com.sk89q.worldedit.util.command.CommandMapping;
import com.sk89q.worldedit.util.command.parametric.AParametricCallable;
import com.sk89q.worldedit.util.eventbus.Subscribe;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
import com.sk89q.worldedit.util.logging.DynamicStreamHandler;
import com.sk89q.worldedit.util.logging.LogFormat;
import com.sk89q.worldedit.world.World;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.enginehub.piston.Command;
import org.enginehub.piston.CommandManager;
import org.enginehub.piston.TextConfig;
import org.enginehub.piston.converter.ArgumentConverters;
import org.enginehub.piston.exception.CommandException;
import org.enginehub.piston.exception.CommandExecutionException;
import org.enginehub.piston.exception.ConditionFailedException;
import org.enginehub.piston.exception.UsageException;
import org.enginehub.piston.gen.CommandRegistration;
import org.enginehub.piston.impl.CommandManagerServiceImpl;
import org.enginehub.piston.inject.InjectedValueStore;
import org.enginehub.piston.inject.Key;
import org.enginehub.piston.inject.MapBackedValueStore;
import org.enginehub.piston.inject.MemoizingValueAccess;
import org.enginehub.piston.inject.MergedValueAccess;
import org.enginehub.piston.part.SubCommandPart;
import org.enginehub.piston.suggestion.Suggestion;
import org.enginehub.piston.util.HelpGenerator;
import org.enginehub.piston.util.ValueProvider;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handles the registration and invocation of commands.
*
* <p>This class is primarily for internal usage.</p>
*/
public final class PlatformCommandManager {
public static final Pattern COMMAND_CLEAN_PATTERN = Pattern.compile("^[/]+");
private static final Logger log = LoggerFactory.getLogger(PlatformCommandManager.class);
private static final java.util.logging.Logger COMMAND_LOG =
java.util.logging.Logger.getLogger("com.sk89q.worldedit.CommandLog");
static {
TextConfig.setCommandPrefix("/");
}
private final WorldEdit worldEdit;
private final PlatformManager platformManager;
private final CommandManagerServiceImpl commandManagerService;
private final CommandManager commandManager;
private final InjectedValueStore globalInjectedValues;
private final DynamicStreamHandler dynamicHandler = new DynamicStreamHandler();
private final WorldEditExceptionConverter exceptionConverter;
private final CommandRegistrationHandler registration;
private static PlatformCommandManager INSTANCE;
/**
* Create a new instance.
*
* @param worldEdit the WorldEdit instance
*/
public PlatformCommandManager(final WorldEdit worldEdit, PlatformManager platformManager) {
checkNotNull(worldEdit);
checkNotNull(platformManager);
INSTANCE = this;
this.worldEdit = worldEdit;
this.platformManager = platformManager;
this.exceptionConverter = new WorldEditExceptionConverter(worldEdit);
this.commandManagerService = new CommandManagerServiceImpl();
this.commandManager = commandManagerService.newCommandManager();
this.globalInjectedValues = MapBackedValueStore.create();
this.registration = new CommandRegistrationHandler(
ImmutableList.of(
new CommandLoggingHandler(worldEdit, COMMAND_LOG)
));
// setup separate from main constructor
// ensures that everything is definitely assigned
initialize();
}
private void initialize() {
// Register this instance for command events
worldEdit.getEventBus().register(this);
// Setup the logger
COMMAND_LOG.addHandler(dynamicHandler);
// Set up the commands manager
registerAlwaysInjectedValues();
registerArgumentConverters();
registerAllCommands();
}
private void registerArgumentConverters() {
DirectionVectorConverter.register(worldEdit, commandManager);
DirectionConverter.register(worldEdit, commandManager);
FactoryConverter.register(worldEdit, commandManager);
for (int count = 2; count <= 3; count++) {
commandManager.registerConverter(Key.of(double.class, Annotations.radii(count)),
CommaSeparatedValuesConverter.wrapAndLimit(ArgumentConverters.get(
TypeToken.of(double.class)
), count)
);
}
VectorConverter.register(commandManager);
EnumConverter.register(commandManager);
RegistryConverter.register(commandManager);
ZonedDateTimeConverter.register(commandManager);
BooleanConverter.register(commandManager);
EntityRemoverConverter.register(commandManager);
RegionFactoryConverter.register(commandManager);
}
private void registerAlwaysInjectedValues() {
globalInjectedValues.injectValue(Key.of(Region.class, Selection.class),
context -> {
LocalSession localSession = context.injectedValue(Key.of(LocalSession.class))
.orElseThrow(() -> new IllegalStateException("No LocalSession"));
return context.injectedValue(Key.of(Player.class))
.map(player -> {
try {
return localSession.getSelection(player.getWorld());
} catch (IncompleteRegionException e) {
exceptionConverter.convert(e);
throw new AssertionError("Should have thrown a new exception.");
}
});
});
globalInjectedValues.injectValue(Key.of(EditSession.class),
context -> {
LocalSession localSession = context.injectedValue(Key.of(LocalSession.class))
.orElseThrow(() -> new IllegalStateException("No LocalSession"));
return context.injectedValue(Key.of(Player.class))
.map(player -> {
EditSession editSession = localSession.createEditSession(player);
editSession.enableStandardMode();
return editSession;
});
});
}
private <CI> void registerSubCommands(String name, List<String> aliases, String desc,
CommandRegistration<CI> registration, CI instance) {
registerSubCommands(name, aliases, desc, registration, instance, m -> {});
}
private <CI> void registerSubCommands(String name, List<String> aliases, String desc,
CommandRegistration<CI> registration, CI instance,
Consumer<CommandManager> additionalConfig) {
commandManager.register(name, cmd -> {
cmd.aliases(aliases);
cmd.description(TextComponent.of(desc));
cmd.action(Command.Action.NULL_ACTION);
CommandManager manager = commandManagerService.newCommandManager();
this.registration.register(
manager,
registration,
instance
);
additionalConfig.accept(manager);
final List<Command> subCommands = manager.getAllCommands().collect(Collectors.toList());
cmd.addPart(SubCommandPart.builder(TranslatableComponent.of("worldedit.argument.action"),
TextComponent.of("Sub-command to run."))
.withCommands(subCommands)
.required()
.build());
cmd.condition(new SubCommandPermissionCondition.Generator(subCommands).build());
});
}
private void registerAllCommands() {
if (Settings.IMP.ENABLED_COMPONENTS.COMMANDS) {
registerSubCommands(
"schematic",
ImmutableList.of("schem", "/schematic", "/schem"),
"Schematic commands for saving/loading areas",
SchematicCommandsRegistration.builder(),
new SchematicCommands(worldEdit)
);
registerSubCommands(
"snapshot",
ImmutableList.of("snap"),
"Snapshot commands for restoring backups",
SnapshotCommandsRegistration.builder(),
new SnapshotCommands(worldEdit)
);
registerSubCommands(
"superpickaxe",
ImmutableList.of("pickaxe", "sp"),
"Super-pickaxe commands",
SuperPickaxeCommandsRegistration.builder(),
new SuperPickaxeCommands(worldEdit)
);
registerSubCommands(
"brush",
ImmutableList.of("br", "/brush", "/br"),
"Brushing commands",
BrushCommandsRegistration.builder(),
new BrushCommands(worldEdit),
manager -> {
PaintBrushCommands.register(commandManagerService, manager, registration);
ApplyBrushCommands.register(commandManagerService, manager, registration);
}
);
registerSubCommands(
"worldedit",
ImmutableList.of("we"),
"WorldEdit commands",
WorldEditCommandsRegistration.builder(),
new WorldEditCommands(worldEdit)
);
this.registration.register(
commandManager,
BiomeCommandsRegistration.builder(),
new BiomeCommands()
);
this.registration.register(
commandManager,
ChunkCommandsRegistration.builder(),
new ChunkCommands(worldEdit)
);
this.registration.register(
commandManager,
ClipboardCommandsRegistration.builder(),
new ClipboardCommands(worldEdit)
);
this.registration.register(
commandManager,
GeneralCommandsRegistration.builder(),
new GeneralCommands(worldEdit)
);
this.registration.register(
commandManager,
GenerationCommandsRegistration.builder(),
new GenerationCommands(worldEdit)
);
this.registration.register(
commandManager,
HistoryCommandsRegistration.builder(),
new HistoryCommands(worldEdit)
);
this.registration.register(
commandManager,
NavigationCommandsRegistration.builder(),
new NavigationCommands(worldEdit)
);
this.registration.register(
commandManager,
RegionCommandsRegistration.builder(),
new RegionCommands(worldEdit)
);
this.registration.register(
commandManager,
ScriptingCommandsRegistration.builder(),
new ScriptingCommands(worldEdit)
);
this.registration.register(
commandManager,
SelectionCommandsRegistration.builder(),
new SelectionCommands(worldEdit)
);
ExpandCommands.register(registration, commandManager, commandManagerService);
this.registration.register(
commandManager,
SnapshotUtilCommandsRegistration.builder(),
new SnapshotUtilCommands(worldEdit)
);
this.registration.register(
commandManager,
ToolCommandsRegistration.builder(),
new ToolCommands(worldEdit)
);
this.registration.register(
commandManager,
ToolUtilCommandsRegistration.builder(),
new ToolUtilCommands(worldEdit)
);
this.registration.register(
commandManager,
UtilityCommandsRegistration.builder(),
new UtilityCommands(worldEdit)
);
this.registration.register(
commandManager,
AnvilCommandsRegistration.builder(),
new AnvilCommands(worldEdit)
);
}
}
// /**
// * Initialize the dispatcher
// */
// public synchronized void setupDispatcher() {
// if (Settings.IMP.ENABLED_COMPONENTS.COMMANDS) {
// DispatcherNode graph = new CommandGraph().builder(builder).commands();
//
// for (Map.Entry<Object, String[]> entry : methodMap.entrySet()) {
// // add command
// String[] aliases = entry.getValue();
// if (aliases.length == 0) {
// graph = graph.registerMethods(entry.getKey());
// } else {
// graph = graph.group(aliases).registerMethods(entry.getKey()).parent();
// }
// }
//
// for (Map.Entry<CommandCallable, String[][]> entry : commandMap.entrySet()) {
// String[][] aliases = entry.getValue();
// CommandCallable callable = entry.getKey();
// if (aliases[0].length == 0) {
// graph = graph.register(callable, aliases[1]);
// } else {
// graph = graph.group(aliases[0]).register(callable, aliases[1]).parent();
// }
// }
//
// commandMap.clear();
// methodMap.clear();
//
// dispatcher = graph
// .group("/anvil")
// .describeAs("Anvil command")
// .registerMethods(new AnvilCommands(worldEdit)).parent()
// .registerMethods(new CFICommand(worldEdit, builder))
// .registerMethods(new OptionsCommands(worldEdit))
// .registerMethods(new BrushOptionsCommands(worldEdit))
// .registerMethods(new BrushOptionsCommands(worldEdit))
// .register(adapt(new ShapedBrushCommand(new DeformCommand(), "worldedit.brush.deform")), "deform")
// .register(adapt(new ShapedBrushCommand(new ApplyCommand(new ReplaceParser(), "Set all blocks within region"), "worldedit.brush.set")), "set")
// .register(adapt(new ShapedBrushCommand(new PaintCommand(), "worldedit.brush.paint")), "paint")
// .register(adapt(new ShapedBrushCommand(new ApplyCommand(), "worldedit.brush.apply")), "apply")
// .register(adapt(new ShapedBrushCommand(new PaintCommand(new TreeGeneratorParser("treeType")), "worldedit.brush.forest")), "forest")
// .register(adapt(new ShapedBrushCommand(ProvidedValue.create(new Deform("y-=1", Mode.RAW_COORD), "Raise one block"), "worldedit.brush.raise")), "raise")
// .register(adapt(new ShapedBrushCommand(ProvidedValue.create(new Deform("y+=1", Mode.RAW_COORD), "Lower one block"), "worldedit.brush.lower")), "lower")
// .parent()
// .group("superpickaxe", "pickaxe", "sp").describeAs("Super-pickaxe commands")
// .registerMethods(new SuperPickaxeCommands(worldEdit))
// .parent().graph().getDispatcher();
//
// if (platform != null) {
// platform.registerCommands(dispatcher);
// }
// }
// }
public static PlatformCommandManager getInstance() {
return INSTANCE;
}
public ExceptionConverter getExceptionConverter() {
return exceptionConverter;
}
void registerCommandsWith(Platform platform) {
log.info("Registering commands with " + platform.getClass().getCanonicalName());
// Delay command registration to allow time for other plugins to hook into FAWE
try {
new CommandScriptLoader().load();
} catch (Throwable e) {
e.printStackTrace();
}
LocalConfiguration config = platform.getConfiguration();
boolean logging = config.logCommands;
String path = config.logFile;
// Register log
if (!logging || path.isEmpty()) {
dynamicHandler.setHandler(null);
COMMAND_LOG.setLevel(Level.OFF);
} else {
File file = new File(config.getWorkingDirectory(), path);
COMMAND_LOG.setLevel(Level.ALL);
log.info("Logging WorldEdit commands to " + file.getAbsolutePath());
try {
dynamicHandler.setHandler(new FileHandler(file.getAbsolutePath(), true));
} catch (IOException e) {
log.warn("Could not use command log file " + path + ": " + e.getMessage());
}
dynamicHandler.setFormatter(new LogFormat(config.logFormat));
}
platform.registerCommands(commandManager);
}
void removeCommands() {
dynamicHandler.setHandler(null);
}
private Stream<Substring> parseArgs(String input) {
return new CommandArgParser(CommandArgParser.spaceSplit(input.substring(1))).parseArgs();
}
@Subscribe
public void handleCommand(CommandEvent event) {
Request.reset();
Actor actor = event.getActor();
if (actor instanceof Player) {
actor = LocationMaskedPlayerWrapper.wrap((Player) actor);
}
String args = event.getArguments();
CommandEvent finalEvent = new CommandEvent(actor, args);
final FawePlayer<Object> fp = FawePlayer.wrap(actor);
TaskManager.IMP.taskNow(() -> {
int space0 = args.indexOf(' ');
String arg0 = space0 == -1 ? args : args.substring(0, space0);
Optional<Command> cmd = commandManager.getCommand(arg0);
if (cmd.isPresent()) {
if (!cmd.queued()) {
handleCommandOnCurrentThread(finalEvent);
return;
}
}
if (!fp.runAction(
() -> PlatformCommandManager.this.handleCommandOnCurrentThread(finalEvent), false, true)) {
BBC.WORLDEDIT_COMMAND_LIMIT.send(fp);
}
finalEvent.setCancelled(true);
}, Fawe.isMainThread());
}
public void handleCommandOnCurrentThread(CommandEvent event) {
Actor actor = platformManager.createProxyActor(event.getActor());
String[] split = parseArgs(event.getArguments())
.map(Substring::getSubstring)
.toArray(String[]::new);
// No command found!
if (!commandManager.containsCommand(split[0])) {
return;
}
LocalSession session = worldEdit.getSessionManager().get(actor);
Request.request().setSession(session);
if (actor instanceof Entity) {
Extent extent = ((Entity) actor).getExtent();
if (extent instanceof World) {
Request.request().setWorld(((World) extent));
}
}
LocalConfiguration config = worldEdit.getConfiguration();
MemoizingValueAccess context = initializeInjectedValues(event::getArguments, actor);
final FawePlayer fp = FawePlayer.wrap(actor);
if (fp == null) {
throw new IllegalArgumentException("FAWE doesn't support: " + actor);
}
ThrowableSupplier<Throwable> task =
() -> commandManager.execute(context,Lists.newArrayList(split));
handleCommandTask(task, context, actor, session, event);
}
public Object handleCommandTask(ThrowableSupplier<Throwable> task,
MemoizingValueAccess context, @NotNull
Actor actor, @Nullable LocalSession session, CommandEvent event) {
String[] split = parseArgs(event.getArguments())
.map(Substring::getSubstring)
.toArray(String[]::new);
Request.reset();
long start = System.currentTimeMillis();
try {
// This is a bit of a hack, since the call method can only throw CommandExceptions
// everything needs to be wrapped at least once. Which means to handle all WorldEdit
// exceptions without writing a hook into every dispatcher, we need to unwrap these
// exceptions and rethrow their converted form, if their is one.
try {
commandManager.execute(context, ImmutableList.copyOf(split));
} catch (Throwable t) {
// Use the exception converter to convert the exception if any of its causes
// can be converted, otherwise throw the original exception
Throwable next = t;
do {
exceptionConverter.convert(next);
next = next.getCause();
} while (next != null);
throw t;
}
} catch (ConditionFailedException e) {
if (e.getCondition() instanceof PermissionCondition) {
actor.printError("You are not permitted to do that. Are you in the right mode?");
} else {
actor.print(e.getRichMessage());
}
} catch (UsageException e) {
actor.print(TextComponent.builder("")
.color(TextColor.RED)
.append(e.getRichMessage())
.build());
ImmutableList<Command> cmd = e.getCommands();
if (!cmd.isEmpty()) {
actor.print(TextComponent.builder("Usage: ")
.color(TextColor.RED)
.append(HelpGenerator.create(e.getCommandParseResult()).getUsage())
.build());
}
} catch (CommandExecutionException e) {
handleUnknownException(actor, e.getCause());
} catch (CommandException e) {
actor.print(TextComponent.builder("")
.color(TextColor.RED)
.append(e.getRichMessage())
.build());
} catch (Throwable t) {
handleUnknownException(actor, t);
} finally {
Optional<EditSession> editSessionOpt =
context.snapshotMemory().injectedValue(Key.of(EditSession.class));
if (editSessionOpt.isPresent()) {
EditSession editSession = editSessionOpt.get();
session.remember(editSession);
editSession.flushQueue();
long time = System.currentTimeMillis() - start;
if (time > 1000) {
BBC.ACTION_COMPLETE.send(actor, time / 1000D);
}
worldEdit.flushBlockBag(actor, editSession);
}
Request.reset();
}
event.setCancelled(true);
return null;
}
private MemoizingValueAccess initializeInjectedValues(Arguments arguments, Actor actor) {
InjectedValueStore store = MapBackedValueStore.create();
store.injectValue(Key.of(Actor.class), ValueProvider.constant(actor));
if (actor instanceof Player) {
store.injectValue(Key.of(Player.class), ValueProvider.constant((Player) actor));
} else {
store.injectValue(Key.of(Player.class), context -> {
throw new CommandException(TextComponent.of("This command must be used with a player."), ImmutableList.of());
});
}
store.injectValue(Key.of(Arguments.class), ValueProvider.constant(arguments));
store.injectValue(Key.of(LocalSession.class),
context -> {
LocalSession localSession = worldEdit.getSessionManager().get(actor);
localSession.tellVersion(actor);
return Optional.of(localSession);
});
return MemoizingValueAccess.wrap(
MergedValueAccess.of(store, globalInjectedValues)
);
}
private void handleUnknownException(Actor actor, Throwable t) {
actor.printError("Please report this error: [See console]");
actor.printRaw(t.getClass().getName() + ": " + t.getMessage());
log.error("An unexpected error while handling a WorldEdit command", t);
}
@Subscribe
public void handleCommandSuggestion(CommandSuggestionEvent event) {
try {
String arguments = event.getArguments();
List<Substring> split = parseArgs(arguments).collect(Collectors.toList());
List<String> argStrings = split.stream()
.map(Substring::getSubstring)
.collect(Collectors.toList());
MemoizingValueAccess access = initializeInjectedValues(() -> arguments, event.getActor());
ImmutableSet<Suggestion> suggestions;
try {
suggestions = commandManager.getSuggestions(access, argStrings);
} catch (Throwable t) { // catch errors which are *not* command exceptions generated by parsers/suggesters
if (!(t instanceof CommandException)) {
log.debug("Unexpected error occurred while generating suggestions for input: " + arguments, t);
return;
}
throw t;
}
event.setSuggestions(suggestions.stream()
.map(suggestion -> {
int noSlashLength = arguments.length() - 1;
Substring original = suggestion.getReplacedArgument() == split.size()
? Substring.from(arguments, noSlashLength, noSlashLength)
: split.get(suggestion.getReplacedArgument());
// increase original points by 1, for removed `/` in `parseArgs`
return Substring.wrap(
suggestion.getSuggestion(),
original.getStart() + 1,
original.getEnd() + 1
);
}).collect(Collectors.toList()));
} catch (ConditionFailedException e) {
if (e.getCondition() instanceof PermissionCondition) {
event.setSuggestions(new ArrayList<>());
}
}
}
/**
* Get the command manager instance.
*
* @return the command manager
*/
public CommandManager getCommandManager() {
return commandManager;
}
}

View File

@ -19,6 +19,8 @@
package com.sk89q.worldedit.extension.platform;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
@ -37,7 +39,12 @@ import com.sk89q.worldedit.command.tool.DoubleActionTraceTool;
import com.sk89q.worldedit.command.tool.Tool;
import com.sk89q.worldedit.command.tool.TraceTool;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.event.platform.*;
import com.sk89q.worldedit.event.platform.BlockInteractEvent;
import com.sk89q.worldedit.event.platform.ConfigurationLoadEvent;
import com.sk89q.worldedit.event.platform.Interaction;
import com.sk89q.worldedit.event.platform.PlatformInitializeEvent;
import com.sk89q.worldedit.event.platform.PlatformReadyEvent;
import com.sk89q.worldedit.event.platform.PlayerInputEvent;
import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
@ -47,10 +54,6 @@ import com.sk89q.worldedit.util.HandSide;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.eventbus.Subscribe;
import com.sk89q.worldedit.world.World;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.Iterator;
@ -58,8 +61,9 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Manages registered {@link Platform}s for WorldEdit. Platforms are
@ -72,7 +76,7 @@ public class PlatformManager {
private static final Logger logger = LoggerFactory.getLogger(PlatformManager.class);
private final WorldEdit worldEdit;
private final CommandManager commandManager;
private final PlatformCommandManager platformCommandManager;
private final List<Platform> platforms = new ArrayList<>();
private final Map<Capability, Platform> preferences = new EnumMap<>(Capability.class);
private @Nullable String firstSeenVersion;
@ -87,7 +91,7 @@ public class PlatformManager {
public PlatformManager(WorldEdit worldEdit) {
checkNotNull(worldEdit);
this.worldEdit = worldEdit;
this.commandManager = new CommandManager(worldEdit, this);
this.platformCommandManager = new PlatformCommandManager(worldEdit, this);
// Register this instance for events
worldEdit.getEventBus().register(this);
@ -110,7 +114,7 @@ public class PlatformManager {
// Make sure that versions are in sync
if (firstSeenVersion != null) {
if (!firstSeenVersion.equals(platform.getVersion())) {
logger.warn("Multiple ports of WorldEdit are installed but they report different versions ({0} and {1}). " +
logger.warn("Multiple ports of WorldEdit are installed but they report different versions ({} and {}). " +
"If these two versions are truly different, then you may run into unexpected crashes and errors.",
new Object[]{ firstSeenVersion, platform.getVersion() });
}
@ -272,8 +276,8 @@ public class PlatformManager {
*
* @return the command manager
*/
public CommandManager getCommandManager() {
return commandManager;
public PlatformCommandManager getPlatformCommandManager() {
return platformCommandManager;
}
/**
@ -301,7 +305,6 @@ public class PlatformManager {
return tool;
}
@SuppressWarnings("deprecation")
@Subscribe
public void handleBlockInteract(BlockInteractEvent event) {
// Create a proxy actor with a potentially different world for
@ -310,112 +313,106 @@ public class PlatformManager {
Location location = event.getLocation();
// At this time, only handle interaction from players
if (!(actor instanceof Player)) {
return;
}
Player player = (Player) actor;
LocalSession session = worldEdit.getSessionManager().get(actor);
Request.reset();
try {
Vector3 vector = location.toVector();
// At this time, only handle interaction from players
if (actor instanceof Player) {
Player player = (Player) actor;
LocalSession session = worldEdit.getSessionManager().get(actor);
Request.reset();
VirtualWorld virtual = session.getVirtualWorld();
if (virtual != null) {
virtual.handleBlockInteract(player, vector.toBlockPoint(), event);
if (event.isCancelled()) return;
}
VirtualWorld virtual = session.getVirtualWorld();
if (virtual != null) {
virtual.handleBlockInteract(player, vector.toBlockPoint(), event);
if (event.isCancelled()) return;
if (event.getType() == Interaction.HIT) {
// superpickaxe is special because its primary interaction is a left click, not a right click
// in addition, it is implicitly bound to all pickaxe items, not just a single tool item
if (player.getItemInHand(HandSide.MAIN_HAND).getType().getId().equals(getConfiguration().wandItem)) {
if (!actor.hasPermission("worldedit.selection.pos")) {
return;
}
FawePlayer<?> fp = FawePlayer.wrap(player);
RegionSelector selector = session.getRegionSelector(player.getWorld());
final Player maskedPlayerWrapper =
new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor),
((Player) actor).getLocation());
BlockVector3 blockPoint = vector.toBlockPoint();
fp.runAction(() -> {
if (selector.selectPrimary(blockPoint,
ActorSelectorLimits.forActor(maskedPlayerWrapper))) {
selector
.explainPrimarySelection(actor, session, blockPoint);
}
}, false, true);
event.setCancelled(true);
return;
}
if (event.getType() == Interaction.HIT) {
if (session.isToolControlEnabled() && player.getItemInHand(HandSide.MAIN_HAND).getType().getId().equals(getConfiguration().wandItem)) {
if (!actor.hasPermission("worldedit.selection.pos")) {
return;
}
if (session.hasSuperPickAxe() && player.isHoldingPickAxe()) {
final BlockTool superPickaxe = session.getSuperPickaxe();
if (superPickaxe != null && superPickaxe.canUse(player)) {
FawePlayer<?> fp = FawePlayer.wrap(player);
final Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation());
fp.runAction(() -> reset(superPickaxe).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location), false, true);
event.setCancelled(true);
return;
}
}
Tool tool = session.getTool(player.getItemInHand(HandSide.MAIN_HAND).getType());
if (tool instanceof DoubleActionBlockTool && tool.canUse(player)) {
FawePlayer<?> fp = FawePlayer.wrap(player);
final Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation());
fp.runAction(() -> reset(((DoubleActionBlockTool) tool)).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location), false, true);
event.setCancelled(true);
}
} else if (event.getType() == Interaction.OPEN) {
if (player.getItemInHand(HandSide.MAIN_HAND).getType().getId().equals(getConfiguration().wandItem)) {
if (!actor.hasPermission("worldedit.selection.pos")) {
return;
}
FawePlayer<?> fp = FawePlayer.wrap(player);
if (fp.checkAction()) {
RegionSelector selector = session.getRegionSelector(player.getWorld());
final Player maskedPlayerWrapper =
new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor),
((Player) actor).getLocation());
Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper(
PlayerWrapper.wrap((Player) actor),
((Player) actor).getLocation());
BlockVector3 blockPoint = vector.toBlockPoint();
fp.runAction(() -> {
if (selector.selectPrimary(blockPoint,
if (selector.selectSecondary(blockPoint,
ActorSelectorLimits.forActor(maskedPlayerWrapper))) {
selector
.explainPrimarySelection(actor, session, blockPoint);
selector.explainSecondarySelection(actor, session,
blockPoint);
}
}, false, true);
event.setCancelled(true);
return;
}
if (session.hasSuperPickAxe() && player.isHoldingPickAxe()) {
final BlockTool superPickaxe = session.getSuperPickaxe();
if (superPickaxe != null && superPickaxe.canUse(player)) {
FawePlayer<?> fp = FawePlayer.wrap(player);
final Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation());
fp.runAction(() -> reset(superPickaxe).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location), false, true);
event.setCancelled(true);
return;
}
}
Tool tool = session.getTool(player);
if (tool instanceof DoubleActionBlockTool) {
if (tool.canUse(player)) {
FawePlayer<?> fp = FawePlayer.wrap(player);
final Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation());
fp.runAction(new Runnable() {
@Override
public void run() {
reset(((DoubleActionBlockTool) tool)).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location);
}
}, false, true);
event.setCancelled(true);
return;
}
}
event.setCancelled(true);
return;
}
} else if (event.getType() == Interaction.OPEN) {
if (session.isToolControlEnabled() && player.getItemInHand(HandSide.MAIN_HAND).getType().getId().equals(getConfiguration().wandItem)) {
if (!actor.hasPermission("worldedit.selection.pos")) {
return;
}
FawePlayer<?> fp = FawePlayer.wrap(player);
if (fp.checkAction()) {
RegionSelector selector = session.getRegionSelector(player.getWorld());
Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper(
PlayerWrapper.wrap((Player) actor),
((Player) actor).getLocation());
BlockVector3 blockPoint = vector.toBlockPoint();
fp.runAction(() -> {
if (selector.selectSecondary(blockPoint,
ActorSelectorLimits.forActor(maskedPlayerWrapper))) {
selector.explainSecondarySelection(actor, session,
blockPoint);
}
}, false, true);
}
event.setCancelled(true);
return;
}
Tool tool = session.getTool(player);
if (tool instanceof BlockTool) {
if (tool.canUse(player)) {
FawePlayer<?> fp = FawePlayer.wrap(player);
if (fp.checkAction()) {
final Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation());
fp.runAction(() -> {
if (tool instanceof BrushTool) {
((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location);
} else {
reset((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location);
}
}, false, true);
event.setCancelled(true);
return;
Tool tool = session.getTool(player);
if (tool instanceof BlockTool && tool.canUse(player)) {
FawePlayer<?> fp = FawePlayer.wrap(player);
if (fp.checkAction()) {
final Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation());
fp.runAction(() -> {
if (tool instanceof BrushTool) {
((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location);
} else {
reset((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location);
}
}
}, false, true);
event.setCancelled(true);
}
}
}
@ -452,9 +449,10 @@ public class PlatformManager {
}
try {
Tool tool = session.getTool(player.getItemInHand(HandSide.MAIN_HAND).getType());
switch (event.getInputType()) {
case PRIMARY: {
if ((getConfiguration().navigationWandMaxDistance > 0) && player.getItemInHand(HandSide.MAIN_HAND).getType().getId().equals(getConfiguration().navigationWand)) {
if (getConfiguration().navigationWandMaxDistance > 0 && player.getItemInHand(HandSide.MAIN_HAND).getType().getId().equals(getConfiguration().navigationWand)) {
if (!player.hasPermission("worldedit.navigation.jumpto.tool")) {
return;
}
@ -470,14 +468,11 @@ public class PlatformManager {
return;
}
Tool tool = session.getTool(player);
if (tool instanceof DoubleActionTraceTool) {
if (tool.canUse(player)) {
FawePlayer<?> fp = FawePlayer.wrap(player);
fp.runAsyncIfFree(() -> reset((DoubleActionTraceTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session));
event.setCancelled(true);
return;
}
if (tool instanceof DoubleActionTraceTool && tool.canUse(player)) {
FawePlayer<?> fp = FawePlayer.wrap(player);
fp.runAsyncIfFree(() -> reset((DoubleActionTraceTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session));
event.setCancelled(true);
return;
}
break;
@ -497,14 +492,12 @@ public class PlatformManager {
return;
}
Tool tool = session.getTool(player);
if (tool instanceof TraceTool) {
if (tool.canUse(player)) {
FawePlayer<?> fp = FawePlayer.wrap(player);
fp.runAction(() -> reset((TraceTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session), false, true);
event.setCancelled(true);
return;
}
if (tool instanceof TraceTool && tool.canUse(player)) {
FawePlayer<?> fp = FawePlayer.wrap(player);
//todo this needs to be fixed so the event is canceled after actPrimary is used and returns true
fp.runAction(() -> reset((TraceTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session), false, true);
event.setCancelled(true);
return;
}
break;

View File

@ -31,6 +31,7 @@ import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.session.SessionKey;
import com.sk89q.worldedit.util.HandSide;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder;
@ -143,6 +144,11 @@ public class PlayerProxy extends AbstractPlayerActor {
basePlayer.printError(msg);
}
@Override
public void print(Component component) {
basePlayer.print(component);
}
@Override
public String[] getGroups() {
return permActor.getGroups();