Add structure format
(for structure blocks)
This commit is contained in:
parent
92ccbfcdcd
commit
d6902866c4
@ -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
|
||||
|
@ -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<String, Object> asMap(Object... pairs) {
|
||||
HashMap<String, Object> map = new HashMap<String, Object>(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<String, Object> value) {
|
||||
HashMap<String, Tag> map = new HashMap<>();
|
||||
for (Map.Entry<String, Object> 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<Tag> 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<Tag> 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);
|
||||
}
|
||||
}
|
||||
|
@ -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<String, Tag> 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<CompoundTag> palette = (List<CompoundTag>) (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<String, Tag> 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<String, Tag> 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<CompoundTag> blocksList = (List<CompoundTag>) (List<?>) tags.get("blocks").getValue();
|
||||
try {
|
||||
for (CompoundTag compound : blocksList) {
|
||||
Map<String, Tag> 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<CompoundTag> entityList = (List<CompoundTag>) (List<?>) entities.getValue();
|
||||
for (CompoundTag entityEntry : entityList) {
|
||||
Map<String, Tag> 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<String, Object> 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<HashMap<String, Object>> 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<String, Object> paletteEntry = new HashMap<>();
|
||||
BundledBlockData.BlockEntry blockData = BundledBlockData.getInstance().findById(block.getId());
|
||||
paletteEntry.put("Name", blockData.id);
|
||||
if (blockData.states != null && !blockData.states.isEmpty()) {
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
loop:
|
||||
for (Map.Entry<String, BundledBlockData.FaweState> stateEntry : blockData.states.entrySet()) {
|
||||
BundledBlockData.FaweState state = stateEntry.getValue();
|
||||
for (Map.Entry<String, BundledBlockData.FaweStateValue> 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<Map<String, Object>> 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<Integer> 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<Map<String, Object>> entities = new ArrayList<>();
|
||||
for (Entity entity : clipboard.getEntities()) {
|
||||
Location loc = entity.getLocation();
|
||||
List<Double> pos = Arrays.asList(loc.getX(), loc.getY(), loc.getZ());
|
||||
List<Integer> blockPos = Arrays.asList(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
|
||||
BaseEntity state = entity.getState();
|
||||
if (state != null) {
|
||||
CompoundTag nbt = state.getNbtData();
|
||||
Map<String, Tag> nbtMap = ReflectionUtils.getMap(nbt.getValue());
|
||||
// Replace rotation data
|
||||
nbtMap.put("Rotation", writeRotation(entity.getLocation(), "Rotation"));
|
||||
nbtMap.put("id", new StringTag(state.getTypeId()));
|
||||
Map<String, Object> 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<DoubleTag> list = new ArrayList<DoubleTag>();
|
||||
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<FloatTag> list = new ArrayList<FloatTag>();
|
||||
list.add(new FloatTag(location.getYaw()));
|
||||
list.add(new FloatTag(location.getPitch()));
|
||||
return new ListTag(FloatTag.class, list);
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* 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.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<String, ClipboardFormat> aliasMap = new HashMap<String, ClipboardFormat>();
|
||||
|
||||
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<String> getAliases() {
|
||||
return Collections.unmodifiableSet(new HashSet<String>(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;
|
||||
}
|
||||
}
|
@ -289,4 +289,7 @@ public class SchematicReader implements ClipboardReader {
|
||||
return expected.cast(test);
|
||||
}
|
||||
|
||||
public static Class<?> inject() {
|
||||
return SchematicReader.class;
|
||||
}
|
||||
}
|
@ -183,12 +183,12 @@ public class BundledBlockData {
|
||||
}
|
||||
|
||||
public static class BlockEntry {
|
||||
private int legacyId;
|
||||
private String id;
|
||||
private String unlocalizedName;
|
||||
private List<String> aliases;
|
||||
private Map<String, FaweState> states = new HashMap<String, FaweState>();
|
||||
private FaweBlockMaterial material = new FaweBlockMaterial();
|
||||
public int legacyId;
|
||||
public String id;
|
||||
public String unlocalizedName;
|
||||
public List<String> aliases;
|
||||
public Map<String, FaweState> states = new HashMap<String, FaweState>();
|
||||
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<String, FaweStateValue> values;
|
||||
|
||||
@Override
|
||||
@ -268,11 +268,10 @@ public class BundledBlockData {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
byte getDataMask() {
|
||||
public byte getDataMask() {
|
||||
return dataMask != null ? dataMask : 0xF;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user