Copy paste/merge FAWE classes to this WorldEdit fork

- so certain people can look at the diff and complain about my sloppy code :(

Signed-off-by: Jesse Boyd <jessepaleg@gmail.com>
This commit is contained in:
Jesse Boyd
2018-08-13 00:03:07 +10:00
parent a920c77cb8
commit a629d15c74
994 changed files with 117583 additions and 10745 deletions

View File

@@ -19,53 +19,112 @@
package com.sk89q.worldedit.extent.clipboard;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
import com.boydti.fawe.object.clipboard.FaweClipboard;
import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard;
import com.boydti.fawe.object.extent.LightingExtent;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.biome.BaseBiome;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockState;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Stores block data as a multi-dimensional array of {@link BaseBlock}s and
* Stores block data as a multi-dimensional array of {@link BlockState}s and
* other data as lists or maps.
*/
public class BlockArrayClipboard implements Clipboard {
public class BlockArrayClipboard implements Clipboard, LightingExtent, Closeable {
private final Region region;
private Region region;
public FaweClipboard IMP;
private Vector size;
private int mx;
private int my;
private int mz;
private Vector origin;
private final BlockStateHolder[][][] blocks;
private final List<ClipboardEntity> entities = new ArrayList<>();
private MutableBlockVector mutable = new MutableBlockVector();
public BlockArrayClipboard(Region region) {
checkNotNull(region);
this.region = region.clone();
this.size = getDimensions();
this.IMP = Settings.IMP.CLIPBOARD.USE_DISK ? new DiskOptimizedClipboard(size.getBlockX(), size.getBlockY(), size.getBlockZ()) : new MemoryOptimizedClipboard(size.getBlockX(), size.getBlockY(), size.getBlockZ());
this.origin = region.getMinimumPoint();
this.mx = origin.getBlockX();
this.my = origin.getBlockY();
this.mz = origin.getBlockZ();
}
/**
* Create a new instance.
*
* <p>
* <p>The origin will be placed at the region's lowest minimum point.</p>
*
* @param region the bounding region
*/
public BlockArrayClipboard(Region region) {
public BlockArrayClipboard(Region region, UUID clipboardId) {
checkNotNull(region);
this.region = region.clone();
this.size = getDimensions();
this.IMP = Settings.IMP.CLIPBOARD.USE_DISK ? new DiskOptimizedClipboard(size.getBlockX(), size.getBlockY(), size.getBlockZ(), clipboardId) : new MemoryOptimizedClipboard(size.getBlockX(), size.getBlockY(), size.getBlockZ());
this.origin = region.getMinimumPoint();
this.mx = origin.getBlockX();
this.my = origin.getBlockY();
this.mz = origin.getBlockZ();
}
Vector dimensions = getDimensions();
blocks = new BlockStateHolder[dimensions.getBlockX()][dimensions.getBlockY()][dimensions.getBlockZ()];
public BlockArrayClipboard(Region region, FaweClipboard clipboard) {
checkNotNull(region);
this.region = region.clone();
this.size = getDimensions();
this.origin = region.getMinimumPoint();
this.mx = origin.getBlockX();
this.my = origin.getBlockY();
this.mz = origin.getBlockZ();
this.IMP = clipboard;
}
public void init(Region region, FaweClipboard fc) {
checkNotNull(region);
checkNotNull(fc);
this.region = region.clone();
this.size = getDimensions();
this.IMP = fc;
this.origin = region.getMinimumPoint();
this.mx = origin.getBlockX();
this.my = origin.getBlockY();
this.mz = origin.getBlockZ();
}
@Override
protected void finalize() throws Throwable {
close();
}
@Override
public void close() {
IMP.close();
}
@Override
@@ -81,6 +140,7 @@ public class BlockArrayClipboard implements Clipboard {
@Override
public void setOrigin(Vector origin) {
this.origin = origin;
IMP.setOrigin(origin.subtract(region.getMinimumPoint()));
}
@Override
@@ -100,8 +160,8 @@ public class BlockArrayClipboard implements Clipboard {
@Override
public List<? extends Entity> getEntities(Region region) {
List<Entity> filtered = new ArrayList<>();
for (Entity entity : entities) {
List<Entity> filtered = new ArrayList<Entity>();
for (Entity entity : getEntities()) {
if (region.contains(entity.getLocation().toVector())) {
filtered.add(entity);
}
@@ -111,62 +171,79 @@ public class BlockArrayClipboard implements Clipboard {
@Override
public List<? extends Entity> getEntities() {
return Collections.unmodifiableList(entities);
return IMP.getEntities();
}
@Nullable
@Override
public Entity createEntity(Location location, BaseEntity entity) {
ClipboardEntity ret = new ClipboardEntity(location, entity);
entities.add(ret);
return ret;
return IMP.createEntity(location.getExtent(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch(), entity);
}
@Override
public BlockState getBlock(Vector position) {
if (region.contains(position)) {
Vector v = position.subtract(region.getMinimumPoint());
BlockStateHolder block = blocks[v.getBlockX()][v.getBlockY()][v.getBlockZ()];
if (block != null) {
return block.toImmutableState();
}
int x = position.getBlockX() - mx;
int y = position.getBlockY() - my;
int z = position.getBlockZ() - mz;
return IMP.getBlock(x, y, z);
}
return EditSession.nullBlock;
}
return BlockTypes.AIR.getDefaultState();
public BlockState getBlockAbs(int x, int y, int z) {
return IMP.getBlock(x, y, z);
}
@Override
public BaseBlock getFullBlock(Vector position) {
if (region.contains(position)) {
Vector v = position.subtract(region.getMinimumPoint());
BlockStateHolder block = blocks[v.getBlockX()][v.getBlockY()][v.getBlockZ()];
if (block != null) {
return block.toBaseBlock();
}
}
return BlockTypes.AIR.getDefaultState().toBaseBlock();
public BlockState getLazyBlock(Vector position) {
return getBlock(position);
}
@Override
public boolean setBlock(Vector position, BlockStateHolder block) throws WorldEditException {
if (region.contains(position)) {
Vector v = position.subtract(region.getMinimumPoint());
blocks[v.getBlockX()][v.getBlockY()][v.getBlockZ()] = block;
return true;
} else {
return false;
public BlockState getFullBlock(Vector position) {
return getLazyBlock(position);
}
@Override
public boolean setBlock(Vector location, BlockStateHolder block) throws WorldEditException {
if (region.contains(location)) {
final int x = location.getBlockX();
final int y = location.getBlockY();
final int z = location.getBlockZ();
return setBlock(x, y, z, block);
}
return false;
}
public boolean setTile(int x, int y, int z, CompoundTag tag) {
x -= mx;
y -= my;
z -= mz;
return IMP.setTile(x, y, z, tag);
}
@Override
public boolean setBlock(int x, int y, int z, BlockStateHolder block) throws WorldEditException {
x -= mx;
y -= my;
z -= mz;
return IMP.setBlock(x, y, z, block);
}
@Override
public BaseBiome getBiome(Vector2D position) {
return new BaseBiome(0);
int x = position.getBlockX() - mx;
int z = position.getBlockZ() - mz;
return IMP.getBiome(x, z);
}
@Override
public boolean setBiome(Vector2D position, BaseBiome biome) {
return false;
int x = position.getBlockX() - mx;
int z = position.getBlockZ() - mz;
IMP.setBiome(x, z, biome.getId());
return true;
}
@Nullable
@@ -175,24 +252,38 @@ public class BlockArrayClipboard implements Clipboard {
return null;
}
/**
* Stores entity data.
*/
private class ClipboardEntity extends StoredEntity {
ClipboardEntity(Location location, BaseEntity entity) {
super(location, entity);
}
@Override
public boolean remove() {
return entities.remove(this);
}
@Nullable
@Override
public <T> T getFacet(Class<? extends T> cls) {
return null;
}
public static Class<?> inject() {
return BlockArrayClipboard.class;
}
}
@Override
public int getLight(int x, int y, int z) {
return getBlockLight(x, y, z);
}
@Override
public int getSkyLight(int x, int y, int z) {
return 0;
}
@Override
public int getBlockLight(int x, int y, int z) {
return getBrightness(x, y, z);
}
@Override
public int getOpacity(int x, int y, int z) {
mutable.mutX(x);
mutable.mutY(y);
mutable.mutZ(z);
return getBlock(mutable).getBlockType().getMaterial().getLightOpacity();
}
@Override
public int getBrightness(int x, int y, int z) {
mutable.mutX(x);
mutable.mutY(y);
mutable.mutZ(z);
return getBlock(mutable).getBlockType().getMaterial().getLightValue();
}
}

View File

@@ -19,13 +19,13 @@
package com.sk89q.worldedit.extent.clipboard;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.util.Location;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* An implementation of {@link Entity} that stores a {@link BaseEntity} with it.
*

View File

@@ -1,147 +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.extent.clipboard.io;
import com.google.common.collect.ImmutableSet;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.jnbt.NamedTag;
import com.sk89q.jnbt.Tag;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
/**
* A collection of supported clipboard formats.
*/
public enum BuiltInClipboardFormat implements ClipboardFormat {
/**
* The Schematic format used by MCEdit.
*/
MCEDIT_SCHEMATIC("mcedit", "mce", "schematic") {
@Override
public String getPrimaryFileExtension() {
return "schematic";
}
@Override
public ClipboardReader getReader(InputStream inputStream) throws IOException {
NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(inputStream));
return new MCEditSchematicReader(nbtStream);
}
@Override
public ClipboardWriter getWriter(OutputStream outputStream) throws IOException {
throw new IOException("This format does not support saving");
}
@Override
public boolean isFormat(File file) {
try (NBTInputStream str = new NBTInputStream(new GZIPInputStream(new FileInputStream(file)))) {
NamedTag rootTag = str.readNamedTag();
if (!rootTag.getName().equals("Schematic")) {
return false;
}
CompoundTag schematicTag = (CompoundTag) rootTag.getTag();
// Check
Map<String, Tag> schematic = schematicTag.getValue();
if (!schematic.containsKey("Materials")) {
return false;
}
} catch (Exception e) {
return false;
}
return true;
}
},
SPONGE_SCHEMATIC("sponge", "schem") {
@Override
public String getPrimaryFileExtension() {
return "schem";
}
@Override
public ClipboardReader getReader(InputStream inputStream) throws IOException {
NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(inputStream));
return new SpongeSchematicReader(nbtStream);
}
@Override
public ClipboardWriter getWriter(OutputStream outputStream) throws IOException {
NBTOutputStream nbtStream = new NBTOutputStream(new GZIPOutputStream(outputStream));
return new SpongeSchematicWriter(nbtStream);
}
@Override
public boolean isFormat(File file) {
try (NBTInputStream str = new NBTInputStream(new GZIPInputStream(new FileInputStream(file)))) {
NamedTag rootTag = str.readNamedTag();
if (!rootTag.getName().equals("Schematic")) {
return false;
}
CompoundTag schematicTag = (CompoundTag) rootTag.getTag();
// Check
Map<String, Tag> schematic = schematicTag.getValue();
if (!schematic.containsKey("Version")) {
return false;
}
} catch (Exception e) {
return false;
}
return true;
}
};
private final ImmutableSet<String> aliases;
BuiltInClipboardFormat(String... aliases) {
this.aliases = ImmutableSet.copyOf(aliases);
}
@Override
public String getName() {
return name();
}
@Override
public Set<String> getAliases() {
return this.aliases;
}
@Override
public Set<String> getFileExtensions() {
return ImmutableSet.of(getPrimaryFileExtension());
}
}

View File

@@ -19,30 +19,425 @@
package com.sk89q.worldedit.extent.clipboard.io;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.jnbt.NBTStreamer;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.clipboard.*;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.boydti.fawe.object.io.PGZIPOutputStream;
import com.boydti.fawe.object.io.ResettableFileInputStream;
import com.boydti.fawe.object.schematic.PNGWriter;
import com.boydti.fawe.object.schematic.Schematic;
import com.boydti.fawe.object.schematic.StructureFormat;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.ReflectionUtils;
import com.google.common.io.ByteSource;
import com.google.common.io.Files;
import com.google.gson.Gson;
import com.sk89q.jnbt.*;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A collection of supported clipboard formats.
*/
public interface ClipboardFormat {
public enum ClipboardFormat {
/**
* Returns the name of this format.
*
* @return The name of the format
* The Schematic format used by many software.
*/
String getName();
@Deprecated
SCHEMATIC(new AbstractClipboardFormat("SCHEMATIC", "mcedit", "mce", "schematic") {
@Override
public ClipboardReader getReader(InputStream inputStream) throws IOException {
if (inputStream instanceof FileInputStream) {
inputStream = new ResettableFileInputStream((FileInputStream) inputStream);
}
BufferedInputStream buffered = new BufferedInputStream(inputStream);
NBTInputStream nbtStream = new NBTInputStream(new BufferedInputStream(new GZIPInputStream(buffered)));
SchematicReader input = new SchematicReader(nbtStream);
input.setUnderlyingStream(inputStream);
return input;
}
@Override
public ClipboardWriter getWriter(OutputStream outputStream) throws IOException {
throw new UnsupportedOperationException("No longer supported.");
}
@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";
}
}),
@Deprecated
SPONGE_SCHEMATIC(new AbstractClipboardFormat("SPONGE", "sponge", "schem") {
@Override
public ClipboardReader getReader(InputStream inputStream) throws IOException {
if (inputStream instanceof FileInputStream) {
inputStream = new ResettableFileInputStream((FileInputStream) inputStream);
}
BufferedInputStream buffered = new BufferedInputStream(inputStream);
NBTInputStream nbtStream = new NBTInputStream(new BufferedInputStream(new GZIPInputStream(buffered)));
SpongeSchematicReader input = new SpongeSchematicReader(nbtStream);
return input;
}
@Override
public ClipboardWriter getWriter(OutputStream outputStream) throws IOException {
OutputStream gzip;
if (outputStream instanceof PGZIPOutputStream || outputStream instanceof GZIPOutputStream) {
gzip = outputStream;
} else {
outputStream = new BufferedOutputStream(outputStream);
PGZIPOutputStream pigz = new PGZIPOutputStream(outputStream);
gzip = pigz;
}
NBTOutputStream nbtStream = new NBTOutputStream(new BufferedOutputStream(gzip));
return new SpongeSchematicWriter(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 "schem";
}
}),
/**
* The structure block format:
* http://minecraft.gamepedia.com/Structure_block_file_format
*/
STRUCTURE(new AbstractClipboardFormat("STRUCTURE", "structure", "nbt") {
@Override
public ClipboardReader getReader(InputStream inputStream) throws IOException {
inputStream = new BufferedInputStream(inputStream);
NBTInputStream nbtStream = new NBTInputStream(new BufferedInputStream(new GZIPInputStream(inputStream)));
return new StructureFormat(nbtStream);
}
@Override
public ClipboardWriter getWriter(OutputStream outputStream) throws IOException {
outputStream = new BufferedOutputStream(outputStream);
OutputStream gzip;
if (outputStream instanceof PGZIPOutputStream || outputStream instanceof GZIPOutputStream) {
gzip = outputStream;
} else {
PGZIPOutputStream pigz = new PGZIPOutputStream(outputStream);
gzip = pigz;
}
NBTOutputStream nbtStream = new NBTOutputStream(new BufferedOutputStream(gzip));
return new StructureFormat(nbtStream);
}
@Override
public boolean isFormat(File file) {
return file.getName().endsWith(".nbt");
}
@Override
public String getExtension() {
return "nbt";
}
}),
/**
* Isometric PNG writer
*/
PNG(new AbstractClipboardFormat("PNG", "png", "image") {
@Override
public ClipboardReader getReader(InputStream inputStream) throws IOException {
return null;
}
@Override
public ClipboardWriter getWriter(OutputStream outputStream) throws IOException {
return new PNGWriter(new BufferedOutputStream(outputStream));
}
@Override
public boolean isFormat(File file) {
return file.getName().endsWith(".png");
}
@Override
public String getExtension() {
return "png";
}
}),
;
private static final Map<String, ClipboardFormat> aliasMap;
static {
aliasMap = new ConcurrentHashMap<>(8, 0.9f, 1);
for (ClipboardFormat emum : ClipboardFormat.values()) {
for (String alias : emum.getAliases()) {
aliasMap.put(alias, emum);
}
}
}
private IClipboardFormat format;
ClipboardFormat() {
}
ClipboardFormat(IClipboardFormat format) {
this.format = format;
}
public URL uploadPublic(final Clipboard clipboard, String category, String user) {
// summary
// blocks
HashMap<String, Object> map = new HashMap<String, Object>();
Vector dimensions = clipboard.getDimensions();
map.put("width", dimensions.getX());
map.put("height", dimensions.getY());
map.put("length", dimensions.getZ());
map.put("creator", user);
if (clipboard instanceof BlockArrayClipboard) {
FaweClipboard fc = ((BlockArrayClipboard) clipboard).IMP;
final int[] ids = new int[BlockTypes.size()];
fc.streamCombinedIds(new NBTStreamer.ByteReader() {
@Override
public void run(int index, int byteValue) {
ids[byteValue & BlockTypes.BIT_MASK]++;
}
});
Map<String, Integer> blocks = new HashMap<String, Integer>();
for (int i = 0; i < ids.length; i++) {
if (ids[i] != 0) {
blocks.put(BlockTypes.get(i).getId(), ids[i]);
}
}
map.put("blocks", blocks);
}
Gson gson = new Gson();
String json = gson.toJson(map);
return MainUtil.upload(Settings.IMP.WEB.ASSETS, false, json, category, null, new RunnableVal<OutputStream>() {
@Override
public void run(OutputStream value) {
write(value, clipboard);
}
});
}
public MultiClipboardHolder loadAllFromInput(Actor player, String input, boolean message) throws IOException {
checkNotNull(player);
checkNotNull(input);
WorldEdit worldEdit = WorldEdit.getInstance();
LocalConfiguration config = worldEdit.getConfiguration();
if (input.startsWith("url:")) {
URL base = new URL(Settings.IMP.WEB.URL);
input = new URL(base, "uploads/" + input.substring(4) + ".schematic").toString();
}
if (input.startsWith("http")) {
URL url = new URL(input);
URL webInterface = new URL(Settings.IMP.WEB.ASSETS);
if (!url.getHost().equalsIgnoreCase(webInterface.getHost())) {
if (message) BBC.WEB_UNAUTHORIZED.send(player, url);
return null;
}
MultiClipboardHolder clipboards = loadAllFromUrl(url);
return clipboards;
} else {
if (input.contains("../") && !player.hasPermission("worldedit.schematic.load.other")) {
if (message) BBC.NO_PERM.send(player, "worldedit.schematic.load.other");
return null;
}
File working = worldEdit.getWorkingDirectoryFile(config.saveDir);
File dir = new File(working, (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS ? (player.getUniqueId().toString() + File.separator) : "") + input);
if (!dir.exists()) {
dir = new File(dir + "." + getExtension());
}
if (!dir.exists()) {
if ((!input.contains("/") && !input.contains("\\")) || player.hasPermission("worldedit.schematic.load.other")) {
dir = new File(worldEdit.getWorkingDirectoryFile(config.saveDir), input);
}
if (!dir.exists()) {
dir = new File(dir + "." + getExtension());
}
}
if (!dir.exists()) {
if (message) BBC.SCHEMATIC_NOT_FOUND.send(player, input);
return null;
}
if (!dir.isDirectory()) {
ByteSource source = Files.asByteSource(dir);
URI uri = dir.toURI();
return new MultiClipboardHolder(uri, new LazyClipboardHolder(dir.toURI(), source, this, null));
}
URIClipboardHolder[] clipboards = loadAllFromDirectory(dir);
if (clipboards.length < 1) {
if (message) BBC.SCHEMATIC_NOT_FOUND.send(player, input);
return null;
}
return new MultiClipboardHolder(dir.toURI(), clipboards);
}
}
public URIClipboardHolder[] loadAllFromDirectory(File dir) {
File[] files = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".schematic");
}
});
LazyClipboardHolder[] clipboards = new LazyClipboardHolder[files.length];
for (int i = 0; i < files.length; i++) {
File file = files[i];
ByteSource source = Files.asByteSource(file);
clipboards[i] = new LazyClipboardHolder(file.toURI(), source, this, null);
}
return clipboards;
}
public MultiClipboardHolder loadAllFromUrl(URL url) throws IOException {
List<LazyClipboardHolder> clipboards = new ArrayList<>();
try (ReadableByteChannel rbc = Channels.newChannel(url.openStream())) {
try (InputStream in = Channels.newInputStream(rbc)) {
try (ZipInputStream zip = new ZipInputStream(in)) {
ZipEntry entry;
byte[] buffer = new byte[8192];
while ((entry = zip.getNextEntry()) != null) {
if (entry.getName().endsWith(".schematic")) {
FastByteArrayOutputStream out = new FastByteArrayOutputStream();
int len = 0;
while ((len = zip.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
byte[] array = out.toByteArray();
ByteSource source = ByteSource.wrap(array);
LazyClipboardHolder clipboard = new LazyClipboardHolder(url.toURI(), source, this, null);
clipboards.add(clipboard);
}
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}
LazyClipboardHolder[] arr = clipboards.toArray(new LazyClipboardHolder[clipboards.size()]);
try {
MultiClipboardHolder multi = new MultiClipboardHolder(url.toURI());
for (LazyClipboardHolder h : arr) multi.add(h);
return multi;
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
private void write(OutputStream value, Clipboard clipboard) {
try {
try (PGZIPOutputStream gzip = new PGZIPOutputStream(value)) {
try (ClipboardWriter writer = format.getWriter(gzip)) {
writer.write(clipboard);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public URL uploadAnonymous(final Clipboard clipboard) {
return MainUtil.upload(null, null, format.getExtension(), new RunnableVal<OutputStream>() {
@Override
public void run(OutputStream value) {
write(value, clipboard);
}
});
}
public IClipboardFormat getFormat() {
return format;
}
/**
* Get a set of aliases.
*
* @return a set of aliases
*/
Set<String> getAliases();
public Set<String> getAliases() {
return format.getAliases();
}
/**
* Create a reader.
@@ -51,7 +446,9 @@ public interface ClipboardFormat {
* @return a reader
* @throws IOException thrown on I/O error
*/
ClipboardReader getReader(InputStream inputStream) throws IOException;
public ClipboardReader getReader(InputStream inputStream) throws IOException {
return format.getReader(inputStream);
}
/**
* Create a writer.
@@ -60,7 +457,57 @@ public interface ClipboardFormat {
* @return a writer
* @throws IOException thrown on I/O error
*/
ClipboardWriter getWriter(OutputStream outputStream) throws IOException;
public ClipboardWriter getWriter(OutputStream outputStream) throws IOException {
return format.getWriter(outputStream);
}
/**
* Set the player's clipboard
* @param player
* @param uri
* @param in
* @return the held clipboard
* @throws IOException
*/
public ClipboardHolder hold(Player player, URI uri, InputStream in) throws IOException {
checkNotNull(player);
checkNotNull(uri);
checkNotNull(in);
final ClipboardReader reader = getReader(in);
final Clipboard clipboard;
LocalSession session = WorldEdit.getInstance().getSessionManager().get(player);
session.setClipboard(null);
if (reader instanceof SchematicReader) {
clipboard = ((SchematicReader) reader).read(player.getUniqueId());
} else if (reader instanceof StructureFormat) {
clipboard = ((StructureFormat) reader).read(player.getUniqueId());
} else {
clipboard = reader.read();
}
URIClipboardHolder holder = new URIClipboardHolder(uri, clipboard);
session.setClipboard(holder);
return holder;
}
public Schematic load(File file) throws IOException {
return load(new FileInputStream(file));
}
public Schematic load(InputStream stream) throws IOException {
return new Schematic(this.getReader(stream).read());
}
/**
* Get the file extension used
*
* @return file extension string
*/
public String getExtension() {
return format.getExtension();
}
/**
* Return whether the given file is of this format.
@@ -68,20 +515,50 @@ public interface ClipboardFormat {
* @param file the file
* @return true if the given file is of this format
*/
boolean isFormat(File file);
public boolean isFormat(File file) {
return format.isFormat(file);
}
/**
* Get the file extension this format primarily uses.
* Find the clipboard format named by the given alias.
*
* @return The primary file extension
* @param alias the alias
* @return the format, otherwise null if none is matched
*/
String getPrimaryFileExtension();
@Nullable
public static ClipboardFormat findByAlias(String alias) {
checkNotNull(alias);
return aliasMap.get(alias.toLowerCase().trim());
}
/**
* Get the file extensions this format is commonly known to use. This should
* include {@link #getPrimaryFileExtension()}.
* Detect the format given a file.
*
* @return The file extensions this format might be known by
* @param file the file
* @return the format, otherwise null if one cannot be detected
*/
Set<String> getFileExtensions();
}
@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 ClipboardFormat addFormat(IClipboardFormat instance) {
ClipboardFormat newEnum = ReflectionUtils.addEnum(ClipboardFormat.class, instance.getName());
newEnum.format = instance;
for (String alias : newEnum.getAliases()) {
aliasMap.put(alias, newEnum);
}
return newEnum;
}
public static Class<?> inject() {
return ClipboardFormat.class;
}
}

View File

@@ -1,125 +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.extent.clipboard.io;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.sk89q.worldedit.WorldEdit;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.annotation.Nullable;
public class ClipboardFormats {
private static final Map<String, ClipboardFormat> aliasMap = new HashMap<>();
private static final Multimap<String, ClipboardFormat> fileExtensionMap = HashMultimap.create();
private static final List<ClipboardFormat> registeredFormats = new ArrayList<>();
public static void registerClipboardFormat(ClipboardFormat format) {
checkNotNull(format);
for (String key : format.getAliases()) {
String lowKey = key.toLowerCase(Locale.ENGLISH);
ClipboardFormat old = aliasMap.put(lowKey, format);
if (old != null) {
aliasMap.put(lowKey, old);
WorldEdit.logger.warning(format.getClass().getName() + " cannot override existing alias '" + lowKey + "' used by " + old.getClass().getName());
}
}
for (String ext : format.getFileExtensions()) {
String lowExt = ext.toLowerCase(Locale.ENGLISH);
fileExtensionMap.put(lowExt, format);
}
registeredFormats.add(format);
}
static {
for (BuiltInClipboardFormat format : BuiltInClipboardFormat.values()) {
registerClipboardFormat(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(Locale.ENGLISH).trim());
}
/**
* Detect the format of 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 : registeredFormats) {
if (format.isFormat(file)) {
return format;
}
}
return null;
}
/**
* @return a multimap from a file extension to the potential matching formats.
*/
public static Multimap<String, ClipboardFormat> getFileExtensionMap() {
return Multimaps.unmodifiableMultimap(fileExtensionMap);
}
public static Collection<ClipboardFormat> getAll() {
return Collections.unmodifiableCollection(registeredFormats);
}
/**
* Not public API, only used by SchematicCommands.
* It is not in SchematicCommands because it may rely on internal register calls.
*/
public static String[] getFileExtensionArray() {
return fileExtensionMap.keySet().toArray(new String[fileExtensionMap.keySet().size()]);
}
private ClipboardFormats() {
}
}

View File

@@ -20,6 +20,7 @@
package com.sk89q.worldedit.extent.clipboard.io;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.world.registry.Registries;
import java.io.Closeable;
import java.io.IOException;

View File

@@ -1,318 +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.extent.clipboard.io;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NamedTag;
import com.sk89q.jnbt.ShortTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NBTCompatibilityHandler;
import com.sk89q.worldedit.extent.clipboard.io.legacycompat.SignCompatibilityHandler;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.entity.EntityTypes;
import com.sk89q.worldedit.world.registry.LegacyMapper;
import com.sk89q.worldedit.world.storage.NBTConversions;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Reads schematic files that are compatible with MCEdit and other editors.
*/
public class MCEditSchematicReader extends NBTSchematicReader {
private static final List<NBTCompatibilityHandler> COMPATIBILITY_HANDLERS = new ArrayList<>();
static {
COMPATIBILITY_HANDLERS.add(new SignCompatibilityHandler());
// TODO Add a handler for skulls, flower pots, note blocks, etc.
}
private static final Logger log = Logger.getLogger(MCEditSchematicReader.class.getCanonicalName());
private final NBTInputStream inputStream;
/**
* Create a new instance.
*
* @param inputStream the input stream to read from
*/
public MCEditSchematicReader(NBTInputStream inputStream) {
checkNotNull(inputStream);
this.inputStream = inputStream;
}
@Override
public Clipboard read() throws IOException {
// Schematic tag
NamedTag rootTag = inputStream.readNamedTag();
if (!rootTag.getName().equals("Schematic")) {
throw new IOException("Tag 'Schematic' does not exist or is not first");
}
CompoundTag schematicTag = (CompoundTag) rootTag.getTag();
// Check
Map<String, Tag> schematic = schematicTag.getValue();
if (!schematic.containsKey("Blocks")) {
throw new IOException("Schematic file is missing a 'Blocks' tag");
}
// Check type of Schematic
String materials = requireTag(schematic, "Materials", StringTag.class).getValue();
if (!materials.equals("Alpha")) {
throw new IOException("Schematic file is not an Alpha schematic");
}
// ====================================================================
// Metadata
// ====================================================================
Vector origin;
Region region;
// Get information
short width = requireTag(schematic, "Width", ShortTag.class).getValue();
short height = requireTag(schematic, "Height", ShortTag.class).getValue();
short length = requireTag(schematic, "Length", ShortTag.class).getValue();
try {
int originX = requireTag(schematic, "WEOriginX", IntTag.class).getValue();
int originY = requireTag(schematic, "WEOriginY", IntTag.class).getValue();
int originZ = requireTag(schematic, "WEOriginZ", IntTag.class).getValue();
Vector min = new Vector(originX, originY, originZ);
int offsetX = requireTag(schematic, "WEOffsetX", IntTag.class).getValue();
int offsetY = requireTag(schematic, "WEOffsetY", IntTag.class).getValue();
int offsetZ = requireTag(schematic, "WEOffsetZ", IntTag.class).getValue();
Vector offset = new Vector(offsetX, offsetY, offsetZ);
origin = min.subtract(offset);
region = new CuboidRegion(min, min.add(width, height, length).subtract(Vector.ONE));
} catch (IOException ignored) {
origin = new Vector(0, 0, 0);
region = new CuboidRegion(origin, origin.add(width, height, length).subtract(Vector.ONE));
}
// ====================================================================
// Blocks
// ====================================================================
// Get blocks
byte[] blockId = requireTag(schematic, "Blocks", ByteArrayTag.class).getValue();
byte[] blockData = requireTag(schematic, "Data", ByteArrayTag.class).getValue();
byte[] addId = new byte[0];
short[] blocks = new short[blockId.length]; // Have to later combine IDs
// We support 4096 block IDs using the same method as vanilla Minecraft, where
// the highest 4 bits are stored in a separate byte array.
if (schematic.containsKey("AddBlocks")) {
addId = requireTag(schematic, "AddBlocks", ByteArrayTag.class).getValue();
}
// Combine the AddBlocks data with the first 8-bit block ID
for (int index = 0; index < blockId.length; index++) {
if ((index >> 1) >= addId.length) { // No corresponding AddBlocks index
blocks[index] = (short) (blockId[index] & 0xFF);
} else {
if ((index & 1) == 0) {
blocks[index] = (short) (((addId[index >> 1] & 0x0F) << 8) + (blockId[index] & 0xFF));
} else {
blocks[index] = (short) (((addId[index >> 1] & 0xF0) << 4) + (blockId[index] & 0xFF));
}
}
}
// Need to pull out tile entities
List<Tag> tileEntities = requireTag(schematic, "TileEntities", ListTag.class).getValue();
Map<BlockVector, Map<String, Tag>> tileEntitiesMap = new HashMap<>();
for (Tag tag : tileEntities) {
if (!(tag instanceof CompoundTag)) continue;
CompoundTag t = (CompoundTag) tag;
int x = 0;
int y = 0;
int z = 0;
Map<String, Tag> values = new HashMap<>();
for (Map.Entry<String, Tag> entry : t.getValue().entrySet()) {
switch (entry.getKey()) {
case "x":
if (entry.getValue() instanceof IntTag) {
x = ((IntTag) entry.getValue()).getValue();
}
break;
case "y":
if (entry.getValue() instanceof IntTag) {
y = ((IntTag) entry.getValue()).getValue();
}
break;
case "z":
if (entry.getValue() instanceof IntTag) {
z = ((IntTag) entry.getValue()).getValue();
}
break;
}
values.put(entry.getKey(), entry.getValue());
}
int index = y * width * length + z * width + x;
BlockState block = LegacyMapper.getInstance().getBlockFromLegacy(blocks[index], blockData[index]);
if (block != null) {
for (NBTCompatibilityHandler handler : COMPATIBILITY_HANDLERS) {
if (handler.isAffectedBlock(block)) {
handler.updateNBT(block, values);
}
}
}
BlockVector vec = new BlockVector(x, y, z);
tileEntitiesMap.put(vec, values);
}
BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
clipboard.setOrigin(origin);
// Don't log a torrent of errors
int failedBlockSets = 0;
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
for (int z = 0; z < length; ++z) {
int index = y * width * length + z * width + x;
BlockVector pt = new BlockVector(x, y, z);
BlockState state = LegacyMapper.getInstance().getBlockFromLegacy(blocks[index], blockData[index]);
try {
if (state != null) {
if (tileEntitiesMap.containsKey(pt)) {
clipboard.setBlock(region.getMinimumPoint().add(pt), state.toBaseBlock(new CompoundTag(tileEntitiesMap.get(pt))));
} else {
clipboard.setBlock(region.getMinimumPoint().add(pt), state);
}
} else {
log.warning("Unknown block when pasting schematic: " + blocks[index] + ":" + blockData[index] + ". Please report this issue.");
}
} catch (WorldEditException e) {
switch (failedBlockSets) {
case 0:
log.log(Level.WARNING, "Failed to set block on a Clipboard", e);
break;
case 1:
log.log(Level.WARNING, "Failed to set block on a Clipboard (again) -- no more messages will be logged", e);
break;
default:
}
failedBlockSets++;
}
}
}
}
// ====================================================================
// Entities
// ====================================================================
try {
List<Tag> entityTags = requireTag(schematic, "Entities", ListTag.class).getValue();
for (Tag tag : entityTags) {
if (tag instanceof CompoundTag) {
CompoundTag compound = (CompoundTag) tag;
String id = convertEntityId(compound.getString("id"));
Location location = NBTConversions.toLocation(clipboard, compound.getListTag("Pos"), compound.getListTag("Rotation"));
if (!id.isEmpty()) {
EntityType entityType = EntityTypes.get(id.toLowerCase());
if (entityType != null) {
BaseEntity state = new BaseEntity(entityType, compound);
clipboard.createEntity(location, state);
} else {
log.warning("Unknown entity when pasting schematic: " + id.toLowerCase());
}
}
}
}
} catch (IOException ignored) { // No entities? No problem
}
return clipboard;
}
private String convertEntityId(String id) {
switch(id) {
case "xp_orb":
return "experience_orb";
case "xp_bottle":
return "experience_bottle";
case "eye_of_ender_signal":
return "eye_of_ender";
case "ender_crystal":
return "end_crystal";
case "fireworks_rocket":
return "firework_rocket";
case "commandblock_minecart":
return "command_block_minecart";
case "snowman":
return "snow_golem";
case "villager_golem":
return "iron_golem";
case "evocation_fangs":
return "evoker_fangs";
case "evocation_illager":
return "evoker";
case "vindication_illager":
return "vindicator";
case "illusion_illager":
return "illusioner";
}
return id;
}
@Override
public void close() throws IOException {
inputStream.close();
}
}

View File

@@ -60,4 +60,4 @@ public abstract class NBTSchematicReader implements ClipboardReader {
return expected.cast(test);
}
}
}

View File

@@ -0,0 +1,114 @@
/*
* 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.Fawe;
import com.boydti.fawe.jnbt.CorruptSchematicStreamer;
import com.boydti.fawe.jnbt.SchematicStreamer;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Reads schematic files based that are compatible with MCEdit and other editors.
*/
public class SchematicReader implements ClipboardReader {
private static final Logger log = Logger.getLogger(SchematicReader.class.getCanonicalName());
private NBTInputStream inputStream;
private InputStream rootStream;
/**
* Create a new instance.
*
* @param inputStream the input stream to read from
*/
public SchematicReader(NBTInputStream inputStream) {
checkNotNull(inputStream);
this.inputStream = inputStream;
}
public void setUnderlyingStream(InputStream in) {
this.rootStream = in;
}
@Override
public Clipboard read() throws IOException {
return read(UUID.randomUUID());
}
public Clipboard read(final UUID clipboardId) throws IOException {
try {
return new SchematicStreamer(inputStream, clipboardId).getClipboard();
} catch (Exception e) {
Fawe.debug("Input is corrupt!");
e.printStackTrace();
return new CorruptSchematicStreamer(rootStream, clipboardId).recover();
}
}
private static <T extends Tag> T requireTag(Map<String, Tag> items, String key, Class<T> expected) throws IOException {
if (!items.containsKey(key)) {
throw new IOException("Schematic file is missing a \"" + key + "\" tag");
}
Tag tag = items.get(key);
if (!expected.isInstance(tag)) {
throw new IOException(key + " tag is not of tag type " + expected.getName());
}
return expected.cast(tag);
}
@Nullable
private static <T extends Tag> T getTag(CompoundTag tag, Class<T> expected, String key) {
Map<String, Tag> items = tag.getValue();
if (!items.containsKey(key)) {
return null;
}
Tag test = items.get(key);
if (!expected.isInstance(test)) {
return null;
}
return expected.cast(test);
}
public static Class<?> inject() {
return SchematicReader.class;
}
@Override
public void close() throws IOException {
inputStream.close();
}
}

View File

@@ -21,6 +21,7 @@ package com.sk89q.worldedit.extent.clipboard.io;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.CompoundTag;
@@ -35,7 +36,8 @@ import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
@@ -43,8 +45,9 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NBTCompatibilityHandler;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -141,12 +144,7 @@ public class SpongeSchematicReader extends NBTSchematicReader {
for (String palettePart : paletteObject.keySet()) {
int id = requireTag(paletteObject, palettePart, IntTag.class).getValue();
BlockState state;
try {
state = WorldEdit.getInstance().getBlockFactory().parseFromInput(palettePart, parserContext).toImmutableState();
} catch (InputParseException e) {
throw new IOException("Invalid BlockState in schematic: " + palettePart + ". Are you missing a mod of using a schematic made in a newer version of Minecraft?");
}
BlockState state = BlockState.get(palettePart);
palette.put(id, state);
}
@@ -154,10 +152,9 @@ public class SpongeSchematicReader extends NBTSchematicReader {
Map<BlockVector, Map<String, Tag>> tileEntitiesMap = new HashMap<>();
try {
List<Map<String, Tag>> tileEntityTags = requireTag(schematic, "TileEntities", ListTag.class).getValue().stream()
.map(tag -> (CompoundTag) tag)
.map(CompoundTag::getValue)
.collect(Collectors.toList());
List<Map<String, Tag>> tileEntityTags = ((ListTag<CompoundTag>) requireTag(schematic, "TileEntities", ListTag.class)).getValue().stream()
.map(CompoundTag::getValue)
.collect(Collectors.toList());
for (Map<String, Tag> tileEntity : tileEntityTags) {
int[] pos = requireTag(tileEntity, "Pos", IntArrayTag.class).getValue();
@@ -196,7 +193,7 @@ public class SpongeSchematicReader extends NBTSchematicReader {
BlockState state = palette.get(value);
BlockVector pt = new BlockVector(x, y, z);
try {
if (tileEntitiesMap.containsKey(pt)) {
if (state.getBlockType().getMaterial().hasContainer() && tileEntitiesMap.containsKey(pt)) {
Map<String, Tag> values = Maps.newHashMap(tileEntitiesMap.get(pt));
for (NBTCompatibilityHandler handler : COMPATIBILITY_HANDLERS) {
if (handler.isAffectedBlock(state)) {
@@ -209,7 +206,7 @@ public class SpongeSchematicReader extends NBTSchematicReader {
values.put("id", values.get("Id"));
values.remove("Id");
values.remove("Pos");
clipboard.setBlock(pt, state.toBaseBlock(new CompoundTag(values)));
clipboard.setBlock(pt, new BaseBlock(state, new CompoundTag(values)));
} else {
clipboard.setBlock(pt, state);
}
@@ -227,4 +224,4 @@ public class SpongeSchematicReader extends NBTSchematicReader {
public void close() throws IOException {
inputStream.close();
}
}
}

View File

@@ -32,9 +32,11 @@ import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -128,7 +130,7 @@ public class SpongeSchematicWriter implements ClipboardWriter {
for (int x = 0; x < width; x++) {
int x0 = min.getBlockX() + x;
BlockVector point = new BlockVector(x0, y0, z0);
BaseBlock block = clipboard.getFullBlock(point);
BlockStateHolder block = clipboard.getFullBlock(point);
if (block.getNbtData() != null) {
Map<String, Tag> values = new HashMap<>();
for (Map.Entry<String, Tag> entry : block.getNbtData().getValue().entrySet()) {
@@ -187,4 +189,4 @@ public class SpongeSchematicWriter implements ClipboardWriter {
public void close() throws IOException {
outputStream.close();
}
}
}

View File

@@ -0,0 +1,557 @@
package com.sk89q.worldedit.extent.clipboard.io;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.collection.SoftHashMap;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import javax.imageio.ImageIO;
public class WavefrontReader implements ClipboardReader {
private final InputStream inputStream;
private final File root;
private final Map<String, BufferedImage> textures = new SoftHashMap<>();
private final Map<String, Map<String, Material>> materialFiles = new HashMap<>();
private final Map<String, Material> materials = new HashMap<>();
public WavefrontReader(File file) throws FileNotFoundException {
this.inputStream = new BufferedInputStream(new FileInputStream(file));
File parent = file.getParentFile();
this.root = parent == null ? new File(".") : parent;
}
private final static double parse(String s) {
int len = s.length();
int index;
int numIndex = 1;
double neg;
if (s.charAt(0) == '-') {
neg = -1;
index = 1;
} else {
index = 0;
neg = 1;
}
double val = 0;
outer:
for (; index < len; index++) {
char c = s.charAt(index);
switch (c) {
case ' ': break outer;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
val = val * 10 + (c - 48);
continue;
case '.': {
double factor = 0.1;
for (; index < len; index++) {
c = s.charAt(index);
switch (c) {
case ' ': break outer;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
val += ((c - 48) * factor);
factor *= 0.1;
}
}
}
break;
}
}
return val * neg;
}
@Override
public Clipboard read() throws IOException {
try (InputStream finalStream = inputStream) {
load(finalStream);
}
return null;
}
private final BufferedImage getTexture(String file) throws IOException {
BufferedImage texture = textures.get(file);
if (texture == null) {
texture = ImageIO.read(new File(root, file));
textures.put(file, texture);
}
return texture;
}
private void readLines(InputStream stream, Consumer<String> onEachLine, boolean nullTerminate) throws IOException {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
if (line.isEmpty() || line.charAt(0) == '#') continue;
onEachLine.accept(line);
}
if (nullTerminate) onEachLine.accept(null);
}
}
private final int toIntColor(float color) {
return (int) (color * 256 + 0.5);
}
private String getFileName(String arg) {
String[] pathSplit = arg.split("[/|\\\\]");
return pathSplit[pathSplit.length - 1];
}
@Override
public void close() throws IOException {
}
private class Material {
private double dissolve = 1;
private int color = Integer.MIN_VALUE;
private String texture;
}
private final void loadMaterials(String fileName) throws IOException {
File file = new File(root, fileName);
if (!file.exists()) {
Fawe.debug(".mtl not found: " + fileName);
return;
}
Map<String, Material> mtl = materialFiles.get(fileName);
if (mtl == null) {
final Map<String, Material> tmp = mtl = new HashMap<>();
materialFiles.put(fileName, tmp);
readLines(new FileInputStream(file), new Consumer<String>() {
private String name;
private Material material;
private int index;
private void add() {
if (material != null) {
if (material.color == Integer.MIN_VALUE) {
material.color = -1;
}
tmp.put(name, material);
material = null;
name = null;
}
}
@Override
public void accept(String s) {
if (s == null) {
add();
return;
}
String[] args = s.split("[ ]+");
switch (args[0]) {
// Name
case "newmtl": {
add();
material = new Material();
name = args[1];
break;
}
// Color
case "Ka":
if (material.color != Integer.MIN_VALUE) break;
case "Kd": {
float r = Float.parseFloat(args[1]);
float g = Float.parseFloat(args[2]);
float b = Float.parseFloat(args[3]);
material.color = (toIntColor(r) << 16) + (toIntColor(g) << 8) + toIntColor(b);
break;
}
// Density
case "d": {
material.dissolve = Double.parseDouble(args[1]);
break;
}
case "Tr": {
material.dissolve = 1.0 - Double.parseDouble(args[1]);
break;
}
case "map_Ka":
if (material.texture != null) break;
case "map_Kd": {
material.texture = getFileName(args[1]);
break;
}
}
}
}, true);
}
materials.putAll(mtl);
}
private final Material getMaterial(String name) {
Material mtl = materials.get(name);
return mtl != null ? mtl : new Material();
}
private void load(InputStream in) throws IOException {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
if (line.isEmpty()) continue;
char char0 = line.charAt(0);
switch (char0) {
case '#': continue;
case 'v':
switch (line.charAt(1)) {
case ' ':
case 'n': {
Double.parseDouble("");
break;
}
case 't': {
}
}
break;
case 'f': {
break;
}
case 'l':
case 's':
case 'o':
case 'g':
// Ignore
break;
default:
String[] args = line.split(" ");
switch (args[0]) {
case "mtllib": {
String[] pathSplit = args[1].split("[/|\\\\]");
String fileName = pathSplit[pathSplit.length - 1];
loadMaterials(fileName);
break;
}
}
}
}
}
//
// final File directory = file.getParentFile();
// final Map<String, SimpleMaterial> materials = new HashMap<String, SimpleMaterial>();
// final Map<Face, BufferedImage> textures = new HashMap<Face, BufferedImage>();
// final Map<Face, Color> colors = new HashMap<Face, Color>();
// final List<Vertex> v = new LinkedList<Vertex>();
// final List<VertexTexture> vt = new LinkedList<VertexTexture>();
// final List<Vertex> vn = new LinkedList<Vertex>();
// final List<Face> f = new LinkedList<Face>();
// final List<String[]> obj = new LinkedList<String[]>();
// for (final String[] entry : obj) {
// if (entry[0].equals("v") || entry[0].equals("vn")) {
// if (entry.length == 1) {
// VLogger.log("[ERROR] Invalid vertex or vertex normal entry found (no data)");
// return null;
// }
// double x;
// double y;
// double z;
// try {
// x = Double.parseDouble(entry[1]);
// y = Double.parseDouble(entry[2]);
// z = Double.parseDouble(entry[3]);
// }
// catch (NumberFormatException | ArrayIndexOutOfBoundsException ex8) {
// final RuntimeException ex5;
// final RuntimeException ex = ex5;
// VLogger.log("[ERROR] Invalid vertex or vertex normal entry found (not parseable data)");
// return null;
// }
// if (entry[0].equals("v")) {
// if (entry.length >= 6) {
// try {
// final float r = Float.parseFloat(entry[4]);
// final float g = Float.parseFloat(entry[5]);
// final float b = Float.parseFloat(entry[6]);
// final Color c = new Color(r, g, b);
// v.add(new Vertex(x, y, z, v.size() + 1, c));
// continue;
// }
// catch (NumberFormatException | ArrayIndexOutOfBoundsException ex9) {
// final RuntimeException ex6;
// final RuntimeException ex = ex6;
// VLogger.log("[ERROR] Invalid vertex color (not parseable data)");
// return null;
// }
// }
// v.add(new Vertex(x, y, z, v.size() + 1));
// }
// else {
// vn.add(new Vertex(x, y, z, vn.size() + 1));
// }
// }
// else {
// if (!entry[0].equals("vt")) {
// continue;
// }
// if (entry.length == 1) {
// VLogger.log("[ERROR] Invalid vertex texture entry found (no data)");
// return null;
// }
// double vt_u;
// double vt_v;
// try {
// vt_u = Double.parseDouble(entry[1]);
// vt_v = Double.parseDouble(entry[2]);
// if (vt_u < 0.0 || vt_v < 0.0 || vt_u > 1.0 || vt_v > 1.0) {
// VLogger.log("[ERROR] UV of vertex texture out of bounds");
// return null;
// }
// }
// catch (NumberFormatException | ArrayIndexOutOfBoundsException ex10) {
// final RuntimeException ex7;
// final RuntimeException ex2 = ex7;
// VLogger.log("[ERROR] Invalid vertex texture entry found (not parseable data)");
// return null;
// }
// vt.add(new VertexTexture(vt_u, vt_v, vn.size() + 1));
// }
// }
// VLogger.log("[IMPORT] Loaded " + v.size() + " vertexes");
// VLogger.log("[IMPORT] Loaded " + vt.size() + " vertex textures");
// VLogger.log("[IMPORT] Loaded " + vn.size() + " vertex normals");
// BufferedImage usemtl_texture = null;
// Color usemtl_color = null;
// for (final String[] entry2 : obj) {
// if (entry2[0].equals("usemtl")) {
// if (entry2.length == 1) {
// VLogger.log("[ERROR] Invalid usemtl entry");
// return null;
// }
// SimpleMaterial material = (SimpleMaterial)materials.get(entry2[1]);
// if (material == null) {
// VLogger.log("[WARN] Material '" + entry2[1] + "' does not exist");
// material = getFallbackMaterial();
// VLogger.log("[WARN] Replacing with fallback material");
// }
// usemtl_texture = material.texture;
// usemtl_color = material.color;
// VLogger.log("[IMPORT] Now using material '" + entry2[1] + "'");
// }
// else {
// if (!entry2[0].equals("f")) {
// continue;
// }
// if (entry2.length == 1) {
// VLogger.log("[ERROR] Invalid face entry (no arguments)");
// return null;
// }
// if (usemtl_texture == null && usemtl_color == null) {
// VLogger.log("[WARN] Current Material has neither a texture nor a color");
// usemtl_color = Colors.getGray(1.0f);
// VLogger.log("[WARN] Using fallback color");
// }
// final Collection<FacePoint> points = new LinkedList<FacePoint>();
// for (int i = 1; i < entry2.length; ++i) {
// final String[] comp = entry2[i].split("/");
// Integer comp_v;
// Integer comp_vt;
// Integer comp_vn;
// try {
// comp_v = Integer.parseInt(comp[0]);
// comp_vt = ((comp.length <= 1 || comp[1].isEmpty()) ? null : Integer.parseInt(comp[1]));
// comp_vn = ((comp.length <= 2 || comp[2].isEmpty()) ? null : Integer.parseInt(comp[2]));
// }
// catch (NumberFormatException ex3) {
// final StringBuilder debug = new StringBuilder();
// String[] array;
// for (int length = (array = comp).length, j = 0; j < length; ++j) {
// final String segment = array[j];
// debug.append(segment);
// debug.append("/");
// }
// VLogger.log("[ERROR] Face point failed to load (" + (Object)debug + ")");
// return null;
// }
// try {
// final FacePoint point = new FacePoint();
// point.v = v.get((int)comp_v - 1);
// point.vt = ((comp_vt == null) ? null : ((VertexTexture)vt.get((int)comp_vt - 1)));
// point.vn = ((comp_vn == null) ? null : ((Vertex)vn.get((int)comp_vn - 1)));
// points.add(point);
// }
// catch (IndexOutOfBoundsException ex4) {
// VLogger.log("[ERROR] Face point reference to missing vertex");
// return null;
// }
// }
// final Face face = new Face(points);
// f.add(face);
// if (usemtl_texture != null) {
// textures.put(face, usemtl_texture);
// }
// if (usemtl_color == null) {
// continue;
// }
// colors.put(face, usemtl_color);
// }
// }
// double minX = Double.MAX_VALUE;
// double minY = Double.MAX_VALUE;
// double minZ = Double.MAX_VALUE;
// double maxX = -1.7976931348623157E308;
// double maxY = -1.7976931348623157E308;
// double maxZ = -1.7976931348623157E308;
// for (final Face face2 : f) {
// for (final FacePoint point2 : face2.points) {
// final double x2 = point2.v.getX();
// final double y2 = point2.v.getY();
// final double z2 = point2.v.getZ();
// if (x2 < minX) {
// minX = x2;
// }
// else if (x2 > maxX) {
// maxX = x2;
// }
// if (y2 < minY) {
// minY = y2;
// }
// else if (y2 > maxY) {
// maxY = y2;
// }
// if (z2 < minZ) {
// minZ = z2;
// }
// else {
// if (z2 <= maxZ) {
// continue;
// }
// maxZ = z2;
// }
// }
// }
// final double size = Math.max(maxX - minX, Math.max(maxY - minY, maxZ - minZ));
// final double scale = size / (resolution - 1.0);
// final List<Face> polygons = new LinkedList<Face>();
// for (final Face face3 : f) {
// polygons.addAll(shatterFace(face3, colors, textures));
// }
// VLogger.log("[IMPORT] " + f.size() + " faces -> " + polygons.size() + " polygons");
// final Map<Position3D, Color> colormap = new HashMap<Position3D, Color>();
// for (final Face poly : polygons) {
// final FacePoint a = (FacePoint)poly.points.get(0);
// final FacePoint b2 = (FacePoint)poly.points.get(1);
// final FacePoint c2 = (FacePoint)poly.points.get(2);
// final Vector3D vAB = new Vector3D((Point3D)a.v, (Point3D)b2.v);
// final Vector3D vAC = new Vector3D((Point3D)a.v, (Point3D)c2.v);
// final float lAB = (float)vAB.length();
// final float lAC = (float)vAC.length();
// double[] array3;
// if (a.vt == null || b2.vt == null) {
// final double[] array2 = array3 = new double[2];
// array2[1] = (array2[0] = 0.0);
// }
// else {
// final double[] array4 = array3 = new double[2];
// array4[0] = b2.vt.u - a.vt.u;
// array4[1] = b2.vt.v - a.vt.v;
// }
// final double[] uvAB = array3;
// double[] array6;
// if (a.vt == null || c2.vt == null) {
// final double[] array5 = array6 = new double[2];
// array5[1] = (array5[0] = 0.0);
// }
// else {
// final double[] array7 = array6 = new double[2];
// array7[0] = c2.vt.u - a.vt.u;
// array7[1] = c2.vt.v - a.vt.v;
// }
// final double[] uvAC = array6;
// double[] array9;
// if (a.vt == null) {
// final double[] array8 = array9 = new double[2];
// array8[1] = (array8[0] = 0.0);
// }
// else {
// final double[] array10 = array9 = new double[2];
// array10[0] = a.vt.u;
// array10[1] = a.vt.v;
// }
// final double[] uvA = array9;
// final Vector3D i2 = vAB.clone();
// i2.normalize();
// i2.multiply(scale);
// final Vector3D i3 = vAC.clone();
// i3.normalize();
// i3.multiply(scale);
// final BufferedImage texture = (BufferedImage)textures.get(poly);
// final Color poly_color = (Color)colors.get(poly);
// final int maxW = (texture == null) ? 0 : (texture.getWidth() - 1);
// final int maxH = (texture == null) ? 0 : (texture.getHeight() - 1);
// final double l = scale / 2.0;
// for (float aloop = 0.0f; aloop < lAB; aloop += l) {
// for (float bloop = 0.0f; bloop < lAC; bloop += l) {
// final float ratio1 = aloop / lAB;
// final float ratio2 = bloop / lAC;
// if (ratio1 + ratio2 > 1.0f) {
// break;
// }
// final Point3D point3 = a.v.clone();
// point3.add(vAB.clone().multiply((double)ratio1));
// point3.add(vAC.clone().multiply((double)ratio2));
// final double colorU = uvA[0] + uvAB[0] * ratio1 + uvAC[0] * ratio2;
// final double colorV = uvA[1] + uvAB[1] * ratio1 + uvAC[1] * ratio2;
// Color pointcolor = null;
// if (texture == null) {
// if (poly.hasVertexColors()) {
// final WeightedColor cA = new WeightedColor(a.v.getColor().getRGB(), 1.0f - ratio1 - ratio2);
// final WeightedColor cB = new WeightedColor(b2.v.getColor().getRGB(), ratio1);
// final WeightedColor cC = new WeightedColor(c2.v.getColor().getRGB(), ratio2);
// pointcolor = Colors.blendColors(new WeightedColor[] { cA, cB, cC });
// }
// else {
// pointcolor = poly_color;
// }
// }
// else {
// pointcolor = new Color(texture.getRGB((int)Math.floor((double)maxW * colorU), (int)Math.floor((double)maxH - maxH * colorV)), true);
// }
// if (pointcolor.getAlpha() != 0) {
// point3.divide(scale);
// colormap.put(point3.toPositionRound(), pointcolor);
// colormap.put(point3.toPositionFloor(), pointcolor);
// }
// }
// }
// }
// VLogger.log("[IMPORT] Import complete, loaded " + f.size() + " faces");
// VLogger.log("[IMPORT] Import complete, created " + colormap.size() + " voxels");
// return new VoxelBox(colormap);
}
}

View File

@@ -20,6 +20,8 @@
package com.sk89q.worldedit.extent.clipboard.io.legacycompat;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.Map;

View File

@@ -26,6 +26,8 @@ import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;