From d6902866c4e22a7b80df91fc894df9b27b24145a Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Fri, 10 Jun 2016 13:10:40 +1000 Subject: [PATCH] Add structure format (for structure blocks) --- core/src/main/java/com/boydti/fawe/Fawe.java | 6 + .../main/java/com/boydti/fawe/FaweCache.java | 150 +++++++++ .../object/schematic/StructureFormat.java | 286 ++++++++++++++++++ .../worldedit/command/SchematicCommands.java | 12 +- .../extent/clipboard/io/ClipboardFormat.java | 222 ++++++++++++++ .../extent/clipboard/io/SchematicReader.java | 3 + .../world/registry/BundledBlockData.java | 29 +- 7 files changed, 691 insertions(+), 17 deletions(-) create mode 100644 core/src/main/java/com/boydti/fawe/object/schematic/StructureFormat.java create mode 100644 core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java diff --git a/core/src/main/java/com/boydti/fawe/Fawe.java b/core/src/main/java/com/boydti/fawe/Fawe.java index b987c755..24af64ea 100644 --- a/core/src/main/java/com/boydti/fawe/Fawe.java +++ b/core/src/main/java/com/boydti/fawe/Fawe.java @@ -34,6 +34,8 @@ import com.sk89q.worldedit.event.extent.EditSessionEvent; import com.sk89q.worldedit.extension.platform.CommandManager; import com.sk89q.worldedit.extension.platform.PlatformManager; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; +import com.sk89q.worldedit.extent.clipboard.io.SchematicReader; import com.sk89q.worldedit.extent.transform.BlockTransformExtent; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.visitor.BreadthFirstSearch; @@ -255,6 +257,10 @@ public class Fawe { SelectionCommand.inject(); // Translations + set optimizations RegionCommands.inject(); // Translations HistoryCommands.inject(); // Translations + // Schematic + SchematicReader.inject(); +// SchematicWriter.inject(); TODO + ClipboardFormat.inject(); // Brushes GravityBrush.inject(); // Fix for instant placement assumption // Selectors diff --git a/core/src/main/java/com/boydti/fawe/FaweCache.java b/core/src/main/java/com/boydti/fawe/FaweCache.java index 2d0ca108..8f99043e 100644 --- a/core/src/main/java/com/boydti/fawe/FaweCache.java +++ b/core/src/main/java/com/boydti/fawe/FaweCache.java @@ -1,8 +1,24 @@ package com.boydti.fawe; import com.boydti.fawe.object.PseudoRandom; +import com.sk89q.jnbt.ByteArrayTag; +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.LongTag; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.CuboidClipboard; import com.sk89q.worldedit.blocks.BaseBlock; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class FaweCache { /** @@ -60,6 +76,33 @@ public class FaweCache { return CACHE_BLOCK[(id << 4) + data]; } + /** + * Get the combined data for a block + * @param id + * @param data + * @return + */ + public static int getCombined(int id, int data) { + return (id << 4) + data; + } + + public static int getId(int combined) { + return combined >> 4; + } + + public static int getData(int combined) { + return combined & 15; + } + + /** + * Get the combined id for a block + * @param block + * @return + */ + public static int getCombined(BaseBlock block) { + return getCombined(block.getId(), block.getData()); + } + static { for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { @@ -249,4 +292,111 @@ public class FaweCache { return false; } } + + public static Map asMap(Object... pairs) { + HashMap map = new HashMap(pairs.length >> 1); + for (int i = 0; i < pairs.length; i+=2) { + String key = (String) pairs[i]; + Object value = pairs[i + 1]; + map.put(key, value); + } + return map; + } + + + public static ShortTag asTag(short value) { + return new ShortTag(value); + } + + public static IntTag asTag(int value) { + return new IntTag(value); + } + + public static DoubleTag asTag(double value) { + return new DoubleTag(value); + } + + public static ByteTag asTag(byte value) { + return new ByteTag(value); + } + + public static FloatTag asTag(float value) { + return new FloatTag(value); + } + + public static LongTag asTag(long value) { + return new LongTag(value); + } + + public static ByteArrayTag asTag(byte[] value) { + return new ByteArrayTag(value); + } + + public static StringTag asTag(String value) { + return new StringTag(value); + } + + public static CompoundTag asTag(Map value) { + HashMap map = new HashMap<>(); + for (Map.Entry entry : value.entrySet()) { + Object child = entry.getValue(); + Tag tag = asTag(child); + map.put(entry.getKey(), tag); + } + return new CompoundTag(map); + } + + public static Tag asTag(Object value) { + if (value instanceof Integer) { + return asTag((int) value); + } else if (value instanceof Short) { + return asTag((short) value); + } else if (value instanceof Double) { + return asTag((double) value); + } else if (value instanceof Byte) { + return asTag((byte) value); + } else if (value instanceof Float) { + return asTag((float) value); + } else if (value instanceof Long) { + return asTag((long) value); + } else if (value instanceof String) { + return asTag((String) value); + } else if (value instanceof Map) { + return asTag((Map) value); + } else if (value instanceof Collection) { + return asTag((Collection) value); + } else if (value instanceof byte[]) { + return asTag((byte[]) value); + } else if (value instanceof Tag) { + return (Tag) value; + } else { + return null; + } + } + + public static ListTag asTag(Object... values) { + Class clazz = null; + List list = new ArrayList<>(); + for (Object value : values) { + Tag tag = asTag(value); + if (clazz == null) { + clazz = tag.getClass(); + } + list.add(tag); + } + return new ListTag(clazz, list); + } + + public static ListTag asTag(Collection values) { + Class clazz = null; + List list = new ArrayList<>(); + for (Object value : values) { + Tag tag = asTag(value); + if (clazz == null) { + clazz = tag.getClass(); + } + list.add(tag); + } + return new ListTag(clazz, list); + } } diff --git a/core/src/main/java/com/boydti/fawe/object/schematic/StructureFormat.java b/core/src/main/java/com/boydti/fawe/object/schematic/StructureFormat.java new file mode 100644 index 00000000..5619e930 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/schematic/StructureFormat.java @@ -0,0 +1,286 @@ +package com.boydti.fawe.object.schematic; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.util.ReflectionUtils; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NBTInputStream; +import com.sk89q.jnbt.NBTOutputStream; +import com.sk89q.jnbt.NamedTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.registry.BundledBlockData; +import com.sk89q.worldedit.world.registry.WorldData; +import com.sk89q.worldedit.world.storage.NBTConversions; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public class StructureFormat implements ClipboardReader, ClipboardWriter { + private static final int MAX_SIZE = Short.MAX_VALUE - Short.MIN_VALUE; + + private NBTInputStream in; + private NBTOutputStream out; + + public StructureFormat(NBTInputStream in) { + this.in = in; + } + + public StructureFormat(NBTOutputStream out) { + this.out = out; + } + + @Override + public Clipboard read(WorldData data) throws IOException { + return read(data, UUID.randomUUID()); + } + + public Clipboard read(WorldData worldData, UUID clipboardId) throws IOException { + NamedTag rootTag = in.readNamedTag(); + if (!rootTag.getName().equals("")) { + throw new IOException("Root tag does not exist or is not first"); + } + Map tags = ((CompoundTag) rootTag.getTag()).getValue(); + + ListTag size = (ListTag) tags.get("size"); + int width = size.getInt(0); + int height = size.getInt(1); + int length = size.getInt(2); + + // Init clipboard + Vector origin = new Vector(0, 0, 0); + CuboidRegion region = new CuboidRegion(origin, origin.add(width, height, length).subtract(Vector.ONE)); + BlockArrayClipboard clipboard = new BlockArrayClipboard(region, clipboardId); + // Blocks + ListTag blocks = (ListTag) tags.get("blocks"); + if (blocks != null) { + // Palette + List palette = (List) (List) tags.get("palette").getValue(); + int[] combinedArray = new int[palette.size()]; + for (int i = 0; i < palette.size(); i++) { + CompoundTag compound = palette.get(i); + Map map = compound.getValue(); + String name = ((StringTag) map.get("Name")).getValue(); + BundledBlockData.BlockEntry blockEntry = BundledBlockData.getInstance().findById(name); + if (blockEntry == null) { + Fawe.debug("Unknown block: " + name); + continue; + } + int id = blockEntry.legacyId; + byte data = (byte) 0; + CompoundTag properties = (CompoundTag) map.get("Properties"); + if (blockEntry.states == null || properties == null || blockEntry.states.isEmpty()) { + combinedArray[i] = FaweCache.getCombined(id, data); + continue; + } + for (Map.Entry property : properties.getValue().entrySet()) { + BundledBlockData.FaweState state = blockEntry.states.get(property.getKey()); + if (state == null) { + System.out.println("Invalid property: " + property.getKey()); + continue; + } + BundledBlockData.FaweStateValue value = state.valueMap().get(((StringTag)property.getValue()).getValue()); + if (value == null) { + System.out.println("Invalid property: " + property.getKey() + ":" + property.getValue()); + continue; + } + data += value.data; + } + combinedArray[i] = FaweCache.getCombined(id, data); + } + // Populate blocks + List blocksList = (List) (List) tags.get("blocks").getValue(); + try { + for (CompoundTag compound : blocksList) { + Map blockMap = compound.getValue(); + IntTag stateTag = (IntTag) blockMap.get("state"); + ListTag posTag = (ListTag) blockMap.get("pos"); + int combined = combinedArray[stateTag.getValue()]; + int id = FaweCache.getId(combined); + int data = FaweCache.getData(combined); + BaseBlock block = FaweCache.getBlock(id, data); + if (FaweCache.hasNBT(id)) { + CompoundTag nbt = (CompoundTag) blockMap.get("nbt"); + if (nbt != null) { + block = new BaseBlock(id, data, nbt); + } + } + clipboard.setBlock(posTag.getInt(0), posTag.getInt(1), posTag.getInt(2), block); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + // Entities + ListTag entities = (ListTag) tags.get("entities"); + if (entities != null) { + List entityList = (List) (List) entities.getValue(); + for (CompoundTag entityEntry : entityList) { + Map entityEntryMap = entityEntry.getValue(); + ListTag posTag = (ListTag) entityEntryMap.get("pos"); + CompoundTag nbtTag = (CompoundTag) entityEntryMap.get("nbt"); + String id = ((StringTag) entityEntryMap.get("Id")).getValue(); + Location location = NBTConversions.toLocation(clipboard, posTag, nbtTag.getListTag("Rotation")); + if (!id.isEmpty()) { + BaseEntity state = new BaseEntity(id, nbtTag); + clipboard.createEntity(location, state); + } + } + } + return clipboard; + } + + @Override + public void write(Clipboard clipboard, WorldData worldData) throws IOException { + write(clipboard, worldData, "FAWE"); + } + + public void write(Clipboard clipboard, WorldData worldData, String owner) throws IOException { + Region region = clipboard.getRegion(); + int width = region.getWidth(); + int height = region.getHeight(); + int length = region.getLength(); + if (width > MAX_SIZE) { + throw new IllegalArgumentException("Width of region too large for a .nbt"); + } + if (height > MAX_SIZE) { + throw new IllegalArgumentException("Height of region too large for a .nbt"); + } + if (length > MAX_SIZE) { + throw new IllegalArgumentException("Length of region too large for a .nbt"); + } + + Map structure = FaweCache.asMap("version", 1, "author", owner); + // ignored: version / owner + Vector mutable = new Vector(0, 0, 0); + int[] indexes = new int[MAX_SIZE]; + // Size + structure.put("size", Arrays.asList(width, height, length)); + // Palette + { + for (int i = 0; i < indexes.length; i++) { + indexes[i] = -1; + } + ArrayList> palette = new ArrayList<>(); + for (Vector point : region) { + BaseBlock block = clipboard.getBlock(point); + int combined = FaweCache.getCombined(block); + int index = indexes[combined]; + if (index != -1) { + continue; + } + indexes[combined] = palette.size(); + HashMap paletteEntry = new HashMap<>(); + BundledBlockData.BlockEntry blockData = BundledBlockData.getInstance().findById(block.getId()); + paletteEntry.put("Name", blockData.id); + if (blockData.states != null && !blockData.states.isEmpty()) { + Map properties = new HashMap<>(); + loop: + for (Map.Entry stateEntry : blockData.states.entrySet()) { + BundledBlockData.FaweState state = stateEntry.getValue(); + for (Map.Entry value : state.valueMap().entrySet()) { + if (value.getValue().isSet(block)) { + String stateName = stateEntry.getKey(); + String stateValue = value.getKey(); + properties.put(stateName, stateValue); + break loop; + } + } + } + paletteEntry.put("Properties", properties); + } + palette.add(paletteEntry); + } + if (!palette.isEmpty()) { + structure.put("palette", palette); + } + } + // Blocks + { + ArrayList> blocks = new ArrayList<>(); + Vector min = region.getMinimumPoint(); + for (Vector point : region) { + BaseBlock block = clipboard.getBlock(point); + int combined = FaweCache.getCombined(block); + int index = indexes[combined]; + List pos = Arrays.asList((int) (point.x - min.x), (int) (point.y - min.y), (int) (point.z - min.z)); + if (!block.hasNbtData()) { + blocks.add(FaweCache.asMap("state", index, "pos", pos)); + } else { + blocks.add(FaweCache.asMap("state", index, "pos", pos, "nbt", block.getNbtData())); + } + } + if (!blocks.isEmpty()) { + structure.put("blocks", blocks); + } + } + // Entities + { + ArrayList> entities = new ArrayList<>(); + for (Entity entity : clipboard.getEntities()) { + Location loc = entity.getLocation(); + List pos = Arrays.asList(loc.getX(), loc.getY(), loc.getZ()); + List blockPos = Arrays.asList(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); + BaseEntity state = entity.getState(); + if (state != null) { + CompoundTag nbt = state.getNbtData(); + Map nbtMap = ReflectionUtils.getMap(nbt.getValue()); + // Replace rotation data + nbtMap.put("Rotation", writeRotation(entity.getLocation(), "Rotation")); + nbtMap.put("id", new StringTag(state.getTypeId())); + Map entityMap = FaweCache.asMap("pos", pos, "blockPos", blockPos, "nbt", nbt); + entities.add(entityMap); + } + } + if (!entities.isEmpty()) { + structure.put("entities", entities); + } + } + out.writeNamedTag("", FaweCache.asTag(structure)); + close(); + } + + @Override + public void close() throws IOException { + if (in != null) { + in.close(); + } + if (out != null) { + out.close(); + } + } + + private Tag writeVector(Vector vector, String name) { + List list = new ArrayList(); + list.add(new DoubleTag(vector.getX())); + list.add(new DoubleTag(vector.getY())); + list.add(new DoubleTag(vector.getZ())); + return new ListTag(DoubleTag.class, list); + } + + private Tag writeRotation(Location location, String name) { + List list = new ArrayList(); + list.add(new FloatTag(location.getYaw())); + list.add(new FloatTag(location.getPitch())); + return new ListTag(FloatTag.class, list); + } +} diff --git a/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index d61e3ca9..fd78aeaf 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.config.BBC; +import com.boydti.fawe.object.schematic.StructureFormat; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; @@ -113,6 +114,8 @@ public class SchematicCommands { final Clipboard clipboard; if (reader instanceof SchematicReader) { clipboard = ((SchematicReader) reader).read(player.getWorld().getWorldData(), player.getUniqueId()); + } else if (reader instanceof StructureFormat) { + clipboard = ((StructureFormat) reader).read(player.getWorld().getWorldData(), player.getUniqueId()); } else { clipboard = reader.read(player.getWorld().getWorldData()); } @@ -137,7 +140,6 @@ public class SchematicCommands { final LocalConfiguration config = this.worldEdit.getConfiguration(); final File dir = this.worldEdit.getWorkingDirectoryFile(config.saveDir); - final File f = this.worldEdit.getSafeSaveFile(player, dir, filename, "schematic", "schematic"); final ClipboardFormat format = ClipboardFormat.findByAlias(formatName); if (format == null) { @@ -145,6 +147,8 @@ public class SchematicCommands { return; } + final File f = this.worldEdit.getSafeSaveFile(player, dir, filename, "schematic", "schematic"); + final ClipboardHolder holder = session.getClipboard(); final Clipboard clipboard = holder.getClipboard(); final Transform transform = holder.getTransform(); @@ -174,7 +178,11 @@ public class SchematicCommands { final FileOutputStream fos = closer.register(new FileOutputStream(f)); final BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos)); final ClipboardWriter writer = closer.register(format.getWriter(bos)); - writer.write(target, holder.getWorldData()); + if (writer instanceof StructureFormat) { + ((StructureFormat) writer).write(target, holder.getWorldData(), player.getName()); + } else { + writer.write(target, holder.getWorldData()); + } log.info(player.getName() + " saved " + f.getCanonicalPath()); BBC.SCHEMATIC_SAVED.send(player, filename); } catch (final IOException e) { diff --git a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java new file mode 100644 index 00000000..1bc38462 --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java @@ -0,0 +1,222 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldedit.extent.clipboard.io; + +import com.boydti.fawe.object.schematic.StructureFormat; +import com.sk89q.jnbt.NBTConstants; +import com.sk89q.jnbt.NBTInputStream; +import com.sk89q.jnbt.NBTOutputStream; + +import javax.annotation.Nullable; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A collection of supported clipboard formats. + */ +public enum ClipboardFormat { + + /** + * The Schematic format used by many software. + */ + SCHEMATIC("mcedit", "mce", "schematic") { + @Override + public ClipboardReader getReader(InputStream inputStream) throws IOException { + NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(inputStream)); + return new SchematicReader(nbtStream); + } + + @Override + public ClipboardWriter getWriter(OutputStream outputStream) throws IOException { + NBTOutputStream nbtStream = new NBTOutputStream(new GZIPOutputStream(outputStream)); + return new SchematicWriter(nbtStream); + } + + @Override + public boolean isFormat(File file) { + DataInputStream str = null; + try { + str = new DataInputStream(new GZIPInputStream(new FileInputStream(file))); + if ((str.readByte() & 0xFF) != NBTConstants.TYPE_COMPOUND) { + return false; + } + byte[] nameBytes = new byte[str.readShort() & 0xFFFF]; + str.readFully(nameBytes); + String name = new String(nameBytes, NBTConstants.CHARSET); + return name.equals("Schematic"); + } catch (IOException e) { + return false; + } finally { + if (str != null) { + try { + str.close(); + } catch (IOException ignored) { + } + } + } + } + + @Override + public String getExtension() { + return "schematic"; + } + }, + // Added + STRUCTURE("structure", "nbt") { + @Override + public ClipboardReader getReader(InputStream inputStream) throws IOException { + NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(inputStream)); + return new StructureFormat(nbtStream); + } + + @Override + public ClipboardWriter getWriter(OutputStream outputStream) throws IOException { + NBTOutputStream nbtStream = new NBTOutputStream(new GZIPOutputStream(outputStream)); + return new StructureFormat(nbtStream); + } + + @Override + public boolean isFormat(File file) { + return file.getName().endsWith(".nbt"); + } + + @Override + public String getExtension() { + return "nbt"; + } + } + + // TODO add the FAWE clipboard / history formats + // .bd, .nbtf, .nbtt, .rabd + + ; + + private static final Map aliasMap = new HashMap(); + + private final String[] aliases; + + /** + * Create a new instance. + * + * @param aliases an array of aliases by which this format may be referred to + */ + private ClipboardFormat(String ... aliases) { + this.aliases = aliases; + } + + /** + * Get a set of aliases. + * + * @return a set of aliases + */ + public Set getAliases() { + return Collections.unmodifiableSet(new HashSet(Arrays.asList(aliases))); + } + + /** + * Create a reader. + * + * @param inputStream the input stream + * @return a reader + * @throws IOException thrown on I/O error + */ + public abstract ClipboardReader getReader(InputStream inputStream) throws IOException; + + /** + * Create a writer. + * + * @param outputStream the output stream + * @return a writer + * @throws IOException thrown on I/O error + */ + public abstract ClipboardWriter getWriter(OutputStream outputStream) throws IOException; + + /** + * Get the file extension used + * @return file extension string + */ + public abstract String getExtension(); + + /** + * Return whether the given file is of this format. + * + * @param file the file + * @return true if the given file is of this format + */ + public abstract boolean isFormat(File file); + + static { + for (ClipboardFormat format : EnumSet.allOf(ClipboardFormat.class)) { + for (String key : format.aliases) { + aliasMap.put(key, format); + } + } + } + + /** + * Find the clipboard format named by the given alias. + * + * @param alias the alias + * @return the format, otherwise null if none is matched + */ + @Nullable + public static ClipboardFormat findByAlias(String alias) { + checkNotNull(alias); + return aliasMap.get(alias.toLowerCase().trim()); + } + + /** + * Detect the format given a file. + * + * @param file the file + * @return the format, otherwise null if one cannot be detected + */ + @Nullable + public static ClipboardFormat findByFile(File file) { + checkNotNull(file); + + for (ClipboardFormat format : EnumSet.allOf(ClipboardFormat.class)) { + if (format.isFormat(file)) { + return format; + } + } + + return null; + } + + public static Class inject() { + return ClipboardFormat.class; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java index 0a8130d6..4cb34220 100644 --- a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java +++ b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java @@ -289,4 +289,7 @@ public class SchematicReader implements ClipboardReader { return expected.cast(test); } + public static Class inject() { + return SchematicReader.class; + } } \ No newline at end of file diff --git a/core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockData.java b/core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockData.java index f292b164..1b524188 100644 --- a/core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockData.java +++ b/core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockData.java @@ -183,12 +183,12 @@ public class BundledBlockData { } public static class BlockEntry { - private int legacyId; - private String id; - private String unlocalizedName; - private List aliases; - private Map states = new HashMap(); - private FaweBlockMaterial material = new FaweBlockMaterial(); + public int legacyId; + public String id; + public String unlocalizedName; + public List aliases; + public Map states = new HashMap(); + public FaweBlockMaterial material = new FaweBlockMaterial(); void postDeserialization() { for (FaweState state : states.values()) { @@ -218,13 +218,13 @@ public class BundledBlockData { } } - class FaweStateValue implements StateValue { + public class FaweStateValue implements StateValue { - private FaweState state; - private Byte data; - private Vector direction; + public FaweState state; + public Byte data; + public Vector direction; - void setState(FaweState state) { + public void setState(FaweState state) { this.state = state; } @@ -250,9 +250,9 @@ public class BundledBlockData { } - class FaweState implements State { + public class FaweState implements State { - private Byte dataMask; + public Byte dataMask; private Map values; @Override @@ -268,11 +268,10 @@ public class BundledBlockData { return value; } } - return null; } - byte getDataMask() { + public byte getDataMask() { return dataMask != null ? dataMask : 0xF; }