Plenty of changes to core block behavior to become more compatible with upstream WorldEdit (still more to be done!)

This commit is contained in:
IronApollo
2019-01-31 10:08:58 -05:00
parent 271b45f3ba
commit e53535319d
116 changed files with 3666 additions and 3774 deletions

View File

@ -81,7 +81,7 @@ public class AbstractDelegateExtent implements LightingExtent {
}
@Override
public BlockState getFullBlock(BlockVector3 position) {
public BaseBlock getFullBlock(BlockVector3 position) {
return extent.getFullBlock(position);
}
@ -138,7 +138,7 @@ public class AbstractDelegateExtent implements LightingExtent {
}
@Override
public boolean setBlock(int x, int y, int z, BlockStateHolder block) throws WorldEditException {
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T block) throws WorldEditException {
// mutable.mutX(x);
// mutable.mutY(y);
// mutable.mutZ(z);
@ -150,7 +150,7 @@ public class AbstractDelegateExtent implements LightingExtent {
}
@Override
public boolean setBlock(BlockVector3 location, BlockStateHolder block) throws WorldEditException {
public <T extends BlockStateHolder<T>> boolean setBlock(BlockVector3 location, T block) throws WorldEditException {
return extent.setBlock(location, block);
}

View File

@ -118,19 +118,19 @@ public interface Extent extends InputExtent, OutputExtent {
@Override
default BlockState getBlock(BlockVector3 position) {
return getFullBlock(position);
return getFullBlock(position).toImmutableState();
}
@Override
default BlockState getLazyBlock(BlockVector3 position) {
return getFullBlock(position);
return getFullBlock(position).toImmutableState();
}
default BlockState getLazyBlock(int x, int y, int z) {
return getLazyBlock(BlockVector3.at(x, y, z));
}
default boolean setBlock(int x, int y, int z, BlockStateHolder state) throws WorldEditException {
default <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T state) throws WorldEditException {
return setBlock(BlockVector3.at(x, y, z), state);
}

View File

@ -80,7 +80,7 @@ public interface InputExtent {
* @param position position of the block
* @return the block
*/
BlockState getFullBlock(BlockVector3 position);
BaseBlock getFullBlock(BlockVector3 position);
/**
* Get the biome at the given location.

View File

@ -50,7 +50,7 @@ public interface OutputExtent {
* @return true if the block was successfully set (return value may not be accurate)
* @throws WorldEditException thrown on an error
*/
boolean setBlock(BlockVector3 position, BlockStateHolder block) throws WorldEditException;
<T extends BlockStateHolder<T>> boolean setBlock(BlockVector3 position, T block) throws WorldEditException;
/**
* Set the biome.

View File

@ -234,8 +234,8 @@ public class BlockArrayClipboard implements Clipboard, LightingExtent, Closeable
//>>>>>>> 399e0ad5... Refactor vector system to be cleaner
@Override
public BlockState getFullBlock(BlockVector3 position) {
return getLazyBlock(position);
public BaseBlock getFullBlock(BlockVector3 position) {
return getLazyBlock(position).toBaseBlock();
}
@Override

View File

@ -19,28 +19,220 @@
package com.sk89q.worldedit.extent.clipboard.io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
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.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import com.boydti.fawe.object.clipboard.AbstractClipboardFormat;
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.StructureFormat;
import com.google.common.collect.ImmutableSet;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.NBTConstants;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.jnbt.NamedTag;
import com.sk89q.jnbt.Tag;
/**
* A collection of supported clipboard formats.
*/
@Deprecated
public class BuiltInClipboardFormat {
public static final ClipboardFormat MCEDIT_SCHEMATIC = ClipboardFormat.SCHEMATIC;
public static final ClipboardFormat SPONGE_SCHEMATIC = ClipboardFormat.SPONGE_SCHEMATIC;
public static final ClipboardFormat STRUCTURE = ClipboardFormat.STRUCTURE;
public static final ClipboardFormat PNG = ClipboardFormat.PNG;
public enum BuiltInClipboardFormat implements ClipboardFormat{
/**
* The Schematic format used by many software.
*/
@Deprecated
public static final ClipboardFormat[] values() {
return ClipboardFormat.values;
}
@Deprecated
public static ClipboardFormat valueOf(String value) {
switch (value) {
case "MCEDIT_SCHEMATIC":
value = "SCHEMATIC";
break;
MCEDIT_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;
}
return ClipboardFormat.valueOf(value);
@Override
public ClipboardWriter getWriter(OutputStream outputStream) throws IOException {
throw new UnsupportedOperationException("No longer supported.");
}
@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;
}
@Override
public String getPrimaryFileExtension() {
return "schematic";
}
},
@Deprecated
SPONGE_SCHEMATIC("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) {
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;
}
@Override
public String getPrimaryFileExtension() {
return "schem";
}
},
/**
* The structure block format:
* http://minecraft.gamepedia.com/Structure_block_file_format
*/
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 getPrimaryFileExtension() {
return "nbt";
}
},
/**
* Isometric PNG writer
*/
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 getPrimaryFileExtension() {
return "png";
}
};
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,275 +19,84 @@
package com.sk89q.worldedit.extent.clipboard.io;
import com.boydti.fawe.config.BBC;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
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.clipboard.URIClipboardHolder;
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.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.math.BlockVector3;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import java.io.*;
import java.lang.reflect.Array;
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.regex.Pattern;
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 enum ClipboardFormat {
/**
* The Schematic format used by many software.
*/
@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) {
if (!file.getName().toLowerCase().endsWith(".schematic")) return false;
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) {
if (!file.getName().toLowerCase().endsWith(".schem")) return false;
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";
}
}),
;
public static final ClipboardFormat[] values;
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);
}
}
values = values();
}
private IClipboardFormat format;
ClipboardFormat() {
}
ClipboardFormat(IClipboardFormat format) {
this.format = format;
}
public interface ClipboardFormat {
/**
* Returns the name of this format.
*
* @return The name of the format
*/
public String getName() {
return name();
}
String getName();
/**
* Get a set of aliases.
*
* @return a set of aliases
*/
Set<String> getAliases();
/**
* Create a reader.
*
* @param inputStream the input stream
* @return a reader
* @throws IOException thrown on I/O error
*/
ClipboardReader getReader(InputStream inputStream) throws IOException;
/**
* Create a writer.
*
* @param outputStream the output stream
* @return a writer
* @throws IOException thrown on I/O error
*/
ClipboardWriter getWriter(OutputStream outputStream) throws IOException;
/**
* Return whether the given file is of this format.
*
* @param file the file
* @return true if the given file is of this format
*/
boolean isFormat(File file);
/**
* Get the file extension this format primarily uses.
*
* @return The primary file extension
*/
public String getPrimaryFileExtension() {
return getExtension();
}
String getPrimaryFileExtension();
/**
* Get the file extensions this format is commonly known to use. This should
@ -295,12 +104,43 @@ public enum ClipboardFormat {
*
* @return The file extensions this format might be known by
*/
public Set<String> getFileExtensions() {
return Collections.singleton(getPrimaryFileExtension());
Set<String> getFileExtensions();
/**
* Set the player's clipboard
* @param player
* @param uri
* @param in
* @return the held clipboard
* @throws IOException
*/
default 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);
clipboard = reader.read(player.getUniqueId());
URIClipboardHolder holder = new URIClipboardHolder(uri, clipboard);
session.setClipboard(holder);
return holder;
}
default Schematic load(File file) throws IOException {
return load(new FileInputStream(file));
}
public URL uploadPublic(final Clipboard clipboard, String category, String user) {
default Schematic load(InputStream stream) throws IOException {
return new Schematic(getReader(stream).read());
}
default URL uploadPublic(final Clipboard clipboard, String category, String user) {
// summary
// blocks
HashMap<String, Object> map = new HashMap<String, Object>();
@ -318,158 +158,20 @@ public enum ClipboardFormat {
}
});
}
public static MultiClipboardHolder loadAllFromInput(Actor player, String input, ClipboardFormat format, boolean message) throws IOException {
checkNotNull(player);
checkNotNull(input);
WorldEdit worldEdit = WorldEdit.getInstance();
LocalConfiguration config = worldEdit.getConfiguration();
if (input.startsWith("url:")) {
if (!player.hasPermission("worldedit.schematic.load.web")) {
if (message) BBC.NO_PERM.send(player, "worldedit.schematic.load.web");
return null;
default URL uploadAnonymous(final Clipboard clipboard) {
return MainUtil.upload(null, null, getPrimaryFileExtension(), new RunnableVal<OutputStream>() {
@Override
public void run(OutputStream value) {
write(value, clipboard);
}
URL base = new URL(Settings.IMP.WEB.URL);
input = new URL(base, "uploads/" + input.substring(4) + ".schematic").toString();
}
if (input.startsWith("http")) {
if (!player.hasPermission("worldedit.schematic.load.asset")) {
if (message) BBC.NO_PERM.send(player, "worldedit.schematic.load.asset");
return null;
}
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 (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS && Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}").matcher(input).find() && !player.hasPermission("worldedit.schematic.load.other")) {
BBC.NO_PERM.send(player, "worldedit.schematic.load.other");
return null;
}
File working = worldEdit.getWorkingDirectoryFile(config.saveDir);
File dir = Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS ? new File(working, player.getUniqueId().toString()) : working;
File f;
if (input.startsWith("#")) {
String[] extensions;
if (format != null) {
extensions = format.getFileExtensions().toArray(new String[0]);
} else {
extensions = ClipboardFormats.getFileExtensionArray();
}
f = player.openFileOpenDialog(extensions);
if (f == null || !f.exists()) {
if (message) player.printError("Schematic " + input + " does not exist! (" + f + ")");
return null;
}
} else {
if (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS && Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}").matcher(input).find() && !player.hasPermission("worldedit.schematic.load.other")) {
if (message) BBC.NO_PERM.send(player, "worldedit.schematic.load.other");
return null;
}
if (format == null && input.matches(".*\\.[\\w].*")) {
String extension = input.substring(input.lastIndexOf('.') + 1, input.length());
format = ClipboardFormat.findByExtension(extension);
}
f = MainUtil.resolve(dir, input, format, true);
}
if (f == null || !f.exists()) {
if (!input.contains("../")) {
dir = worldEdit.getWorkingDirectoryFile(config.saveDir);
f = MainUtil.resolve(dir, input, format, true);
}
}
if (f == null || !f.exists() || !MainUtil.isInSubDirectory(working, f)) {
if (message) player.printError("Schematic " + input + " does not exist! (" + ((f == null) ? false : f.exists()) + "|" + f + "|" + (f == null ? false : !MainUtil.isInSubDirectory(working, f)) + ")");
return null;
}
if (format == null && f.isFile()) {
format = ClipboardFormat.findByFile(f);
if (format == null) {
BBC.CLIPBOARD_INVALID_FORMAT.send(player, f.getName());
return null;
}
}
if (!f.exists()) {
if (message) BBC.SCHEMATIC_NOT_FOUND.send(player, input);
return null;
}
if (!f.isDirectory()) {
ByteSource source = Files.asByteSource(f);
URI uri = f.toURI();
return new MultiClipboardHolder(uri, new LazyClipboardHolder(f.toURI(), source, format, null));
}
URIClipboardHolder[] clipboards = loadAllFromDirectory(f);
if (clipboards.length < 1) {
if (message) BBC.SCHEMATIC_NOT_FOUND.send(player, input);
return null;
}
return new MultiClipboardHolder(f.toURI(), clipboards);
}
}
public static URIClipboardHolder[] loadAllFromDirectory(File dir) {
HashSet<String> extensions = new HashSet<>(Arrays.asList(ClipboardFormats.getFileExtensionArray()));
File[] files = dir.listFiles(pathname -> {
String input = pathname.getName();
String extension = input.substring(input.lastIndexOf('.') + 1, input.length());
return (extensions.contains(extension.toLowerCase()));
});
LazyClipboardHolder[] clipboards = new LazyClipboardHolder[files.length];
for (int i = 0; i < files.length; i++) {
File file = files[i];
ByteSource source = Files.asByteSource(file);
ClipboardFormat format = ClipboardFormat.findByFile(file);
clipboards[i] = new LazyClipboardHolder(file.toURI(), source, format, null);
}
return clipboards;
}
public static 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) {
String filename = entry.getName();
String extension = filename.substring(filename.lastIndexOf('.') + 1, filename.length());
ClipboardFormat format = findByExtension(filename);
if (format != null) {
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, format, 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) {
default void write(OutputStream value, Clipboard clipboard) {
try {
try (PGZIPOutputStream gzip = new PGZIPOutputStream(value)) {
try (ClipboardWriter writer = format.getWriter(gzip)) {
try (ClipboardWriter writer = getWriter(gzip)) {
writer.write(clipboard);
}
}
@ -477,164 +179,4 @@ public enum ClipboardFormat {
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
*/
public Set<String> getAliases() {
return format.getAliases();
}
/**
* Create a reader.
*
* @param inputStream the input stream
* @return a reader
* @throws IOException thrown on I/O error
*/
public ClipboardReader getReader(InputStream inputStream) throws IOException {
return format.getReader(inputStream);
}
/**
* Create a writer.
*
* @param outputStream the output stream
* @return a writer
* @throws IOException thrown on I/O error
*/
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);
clipboard = reader.read(player.getUniqueId());
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.
*
* @param file the file
* @return true if the given file is of this format
*/
public boolean isFormat(File file) {
return format.isFormat(file);
}
/**
* 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());
}
@Nullable
public static ClipboardFormat findByExtension(String extension) {
checkNotNull(extension);
extension = extension.toLowerCase();
for (ClipboardFormat format : values) {
if (format.getFileExtensions().contains(extension)) {
return format;
}
}
return null;
}
/**
* 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 ClipboardFormat addFormat(IClipboardFormat instance) {
ClipboardFormat newEnum = ReflectionUtils.addEnum(ClipboardFormat.class, instance.getName());
newEnum.format = instance;
for (String alias : newEnum.getAliases()) {
aliasMap.put(alias, newEnum);
}
ArrayList<ClipboardFormat> newValues = new ArrayList<>(Arrays.asList(values));
newValues.add(newEnum);
ClipboardFormat[] newValuesArray = newValues.toArray(new ClipboardFormat[newValues.size()]);
try {
ReflectionUtils.setFailsafeFieldValue(ClipboardFormat.class.getDeclaredField("values"), null, newValuesArray);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return newEnum;
}
}

View File

@ -19,17 +19,77 @@
package com.sk89q.worldedit.extent.clipboard.io;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.clipboard.LazyClipboardHolder;
import com.boydti.fawe.object.clipboard.MultiClipboardHolder;
import com.boydti.fawe.object.clipboard.URIClipboardHolder;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.boydti.fawe.util.MainUtil;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.io.ByteSource;
import com.google.common.io.Files;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.Actor;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
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.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.annotation.Nullable;
import java.io.File;
import java.util.*;
import static com.google.common.base.Preconditions.checkNotNull;
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.
*
@ -39,7 +99,8 @@ public class ClipboardFormats {
*/
@Nullable
public static ClipboardFormat findByAlias(String alias) {
return ClipboardFormat.findByAlias(alias);
checkNotNull(alias);
return aliasMap.get(alias.toLowerCase(Locale.ENGLISH).trim());
}
/**
@ -53,7 +114,7 @@ public class ClipboardFormats {
public static ClipboardFormat findByFile(File file) {
checkNotNull(file);
for (ClipboardFormat format : ClipboardFormat.values) {
for (ClipboardFormat format : registeredFormats) {
if (format.isFormat(file)) {
return format;
}
@ -61,22 +122,36 @@ public class ClipboardFormats {
return null;
}
/**
* Detect the format using the given extension
* @param string
* the extension
* @return the format, otherwise null if one cannot be detected
*/
@Nullable
public static ClipboardFormat findByExtension(String extension) {
checkNotNull(extension);
Collection<Entry<String, ClipboardFormat>> entries = getFileExtensionMap().entries();
for(Map.Entry<String, ClipboardFormat> entry : entries) {
if(entry.getKey().equalsIgnoreCase(extension)) {
return entry.getValue();
}
}
return null;
}
/**
* @return a multimap from a file extension to the potential matching formats.
*/
public static Multimap<String, ClipboardFormat> getFileExtensionMap() {
HashMultimap<String, ClipboardFormat> map = HashMultimap.create();
for (ClipboardFormat format : ClipboardFormat.values) {
for (String ext : format.getFileExtensions()) {
map.put(ext, format);
}
}
return map;
return Multimaps.unmodifiableMultimap(fileExtensionMap);
}
public static Collection<ClipboardFormat> getAll() {
return Arrays.asList(ClipboardFormat.values);
return Collections.unmodifiableCollection(registeredFormats);
}
/**
@ -84,13 +159,156 @@ public class ClipboardFormats {
* It is not in SchematicCommands because it may rely on internal register calls.
*/
public static String[] getFileExtensionArray() {
List<String> exts = new ArrayList<>();
HashMultimap<String, ClipboardFormat> map = HashMultimap.create();
for (ClipboardFormat format : ClipboardFormat.values) {
exts.addAll(format.getFileExtensions());
}
return exts.toArray(new String[exts.size()]);
return fileExtensionMap.keySet().toArray(new String[fileExtensionMap.keySet().size()]);
}
private ClipboardFormats() {}
private ClipboardFormats() {
}
public static MultiClipboardHolder loadAllFromInput(Actor player, String input, ClipboardFormat format, boolean message) throws IOException {
checkNotNull(player);
checkNotNull(input);
WorldEdit worldEdit = WorldEdit.getInstance();
LocalConfiguration config = worldEdit.getConfiguration();
if (input.startsWith("url:")) {
if (!player.hasPermission("worldedit.schematic.load.web")) {
if (message) BBC.NO_PERM.send(player, "worldedit.schematic.load.web");
return null;
}
URL base = new URL(Settings.IMP.WEB.URL);
input = new URL(base, "uploads/" + input.substring(4) + ".schematic").toString();
}
if (input.startsWith("http")) {
if (!player.hasPermission("worldedit.schematic.load.asset")) {
if (message) BBC.NO_PERM.send(player, "worldedit.schematic.load.asset");
return null;
}
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 (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS && Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}").matcher(input).find() && !player.hasPermission("worldedit.schematic.load.other")) {
BBC.NO_PERM.send(player, "worldedit.schematic.load.other");
return null;
}
File working = worldEdit.getWorkingDirectoryFile(config.saveDir);
File dir = Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS ? new File(working, player.getUniqueId().toString()) : working;
File f;
if (input.startsWith("#")) {
String[] extensions;
if (format != null) {
extensions = format.getFileExtensions().toArray(new String[0]);
} else {
extensions = ClipboardFormats.getFileExtensionArray();
}
f = player.openFileOpenDialog(extensions);
if (f == null || !f.exists()) {
if (message) player.printError("Schematic " + input + " does not exist! (" + f + ")");
return null;
}
} else {
if (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS && Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}").matcher(input).find() && !player.hasPermission("worldedit.schematic.load.other")) {
if (message) BBC.NO_PERM.send(player, "worldedit.schematic.load.other");
return null;
}
if (format == null && input.matches(".*\\.[\\w].*")) {
String extension = input.substring(input.lastIndexOf('.') + 1, input.length());
format = findByExtension(extension);
}
f = MainUtil.resolve(dir, input, format, true);
}
if (f == null || !f.exists()) {
if (!input.contains("../")) {
dir = worldEdit.getWorkingDirectoryFile(config.saveDir);
f = MainUtil.resolve(dir, input, format, true);
}
}
if (f == null || !f.exists() || !MainUtil.isInSubDirectory(working, f)) {
if (message) player.printError("Schematic " + input + " does not exist! (" + ((f == null) ? false : f.exists()) + "|" + f + "|" + (f == null ? false : !MainUtil.isInSubDirectory(working, f)) + ")");
return null;
}
if (format == null && f.isFile()) {
format = findByFile(f);
if (format == null) {
BBC.CLIPBOARD_INVALID_FORMAT.send(player, f.getName());
return null;
}
}
if (!f.exists()) {
if (message) BBC.SCHEMATIC_NOT_FOUND.send(player, input);
return null;
}
if (!f.isDirectory()) {
ByteSource source = Files.asByteSource(f);
URI uri = f.toURI();
return new MultiClipboardHolder(uri, new LazyClipboardHolder(f.toURI(), source, format, null));
}
URIClipboardHolder[] clipboards = loadAllFromDirectory(f);
if (clipboards.length < 1) {
if (message) BBC.SCHEMATIC_NOT_FOUND.send(player, input);
return null;
}
return new MultiClipboardHolder(f.toURI(), clipboards);
}
}
public static URIClipboardHolder[] loadAllFromDirectory(File dir) {
HashSet<String> extensions = new HashSet<>(Arrays.asList(ClipboardFormats.getFileExtensionArray()));
File[] files = dir.listFiles(pathname -> {
String input = pathname.getName();
String extension = input.substring(input.lastIndexOf('.') + 1, input.length());
return (extensions.contains(extension.toLowerCase()));
});
LazyClipboardHolder[] clipboards = new LazyClipboardHolder[files.length];
for (int i = 0; i < files.length; i++) {
File file = files[i];
ByteSource source = Files.asByteSource(file);
ClipboardFormat format = findByFile(file);
clipboards[i] = new LazyClipboardHolder(file.toURI(), source, format, null);
}
return clipboards;
}
public static 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) {
String filename = entry.getName();
String extension = filename.substring(filename.lastIndexOf('.') + 1, filename.length());
ClipboardFormat format = findByExtension(filename);
if (format != null) {
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, format, 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);
}
}
}

View File

@ -1,335 +1,330 @@
/*
* 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.transform;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.object.extent.ResettableExtent;
import com.boydti.fawe.util.ReflectionUtils;
import com.google.common.collect.Sets;
import com.sk89q.jnbt.ByteTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.helper.MCDirections;
import com.sk89q.worldedit.math.transform.AffineTransform;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.registry.state.AbstractProperty;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Sets;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.math.transform.AffineTransform;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.registry.state.BooleanProperty;
import com.sk89q.worldedit.registry.state.DirectionalProperty;
import com.sk89q.worldedit.registry.state.EnumProperty;
import com.sk89q.worldedit.registry.state.IntegerProperty;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.world.biome.BaseBiome;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
/**
* Transforms blocks themselves (but not their position) according to a
* given transform.
*/
public class BlockTransformExtent extends ResettableExtent {
private Transform transform;
private Transform transformInverse;
private int[] BLOCK_ROTATION_BITMASK;
private int[][] BLOCK_TRANSFORM;
private int[][] BLOCK_TRANSFORM_INVERSE;
private int[] ALL = new int[0];
private Transform transform;
public BlockTransformExtent(Extent parent) {
this(parent, new AffineTransform());
}
public BlockTransformExtent(Extent parent, Transform transform) {
super(parent);
/**
* Create a new instance.
*
* @param extent the extent
*/
public BlockTransformExtent(Extent extent, Transform transform) {
super(extent);
checkNotNull(transform);
this.transform = transform;
this.transformInverse = this.transform.inverse();
cache();
}
private List<Direction> getDirections(AbstractProperty property) {
if (property instanceof DirectionalProperty) {
DirectionalProperty directional = (DirectionalProperty) property;
directional.getValues();
} else {
switch (property.getKey()) {
case HALF:
case ROTATION:
case AXIS:
case FACING:
case SHAPE:
case NORTH:
case EAST:
case SOUTH:
case WEST:
}
}
return null;
/**
* Get the transform.
*
* @return the transform
*/
public Transform getTransform() {
return transform;
}
/**
* Set the transform
* @param affine
*/
public void setTransform(Transform affine) {
this.transform = affine;
}
// @Override
// public BlockState getBlock(BlockVector3 position) {
// return transformBlock(super.getBlock(position), false);
// }
//
// @Override
// public BaseBlock getFullBlock(BlockVector3 position) {
// return transformBlock(super.getFullBlock(position), false);
// }
// @Override
// public boolean setBlock(BlockVector3 location, BlockStateHolder block) throws WorldEditException {
// return super.setBlock(location, transformBlock(block, true));
// }
// /**
// * Transform the given block using the given transform.
// *
// * <p>The provided block is modified.</p>
// *
// * @param block the block
// * @param transform the transform
// * @return the same block
// */
// public static <T extends BlockStateHolder> T transform(T block, Transform transform) {
// return transform(block, transform, block);
// }
/**
* Transform a block without making a copy.
*
* @param block the block
* @param reverse true to transform in the opposite direction
* @return the same block
*/
private <T extends BlockStateHolder<T>> T transformBlock(T block, boolean reverse) {
return transform(block, reverse ? transform.inverse() : transform);
}
@Override
public BlockState getLazyBlock(BlockVector3 position) {
return transformFast(super.getLazyBlock(position)).toImmutableState();
}
@Override
public BlockState getLazyBlock(int x, int y, int z) {
return transformFast(super.getLazyBlock(x, y, z)).toImmutableState();
}
@Override
public BlockState getBlock(BlockVector3 position) {
return transformFast(super.getBlock(position)).toImmutableState();
}
@Override
public BaseBlock getFullBlock(BlockVector3 position) {
return transformFast(super.getFullBlock(position).toImmutableState());
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(int x, int y, int z, B block) throws WorldEditException {
return super.setBlock(x, y, z, transformFastInverse((BlockState)block));
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 location, B block) throws WorldEditException {
return super.setBlock(location, transformFastInverse((BlockState)block));
}
private static final Set<String> directionNames = Sets.newHashSet("north", "south", "east", "west");
// /**
// * Transform the given block using the given transform.
// *
// * @param block the block
// * @param transform the transform
// * @param changedBlock the block to change
// * @return the changed block
// */
// private static <T extends BlockStateHolder> T transform(T block, Transform transform, T changedBlock) {
// checkNotNull(block);
// checkNotNull(transform);
//
// List<? extends Property> properties = block.getBlockType().getProperties();
//
// for (Property property : properties) {
// if (property instanceof DirectionalProperty) {
// Direction value = (Direction) block.getState(property);
// if (value != null) {
// Vector3 newValue = getNewStateValue((DirectionalProperty) property, transform, value.toVector());
// if (newValue != null) {
// changedBlock = (T) changedBlock.with(property, Direction.findClosest(newValue, Direction.Flag.ALL));
// }
// }
// }
// }
/**
* Transform the given block using the given transform.
*
* <p>The provided block is <em>not</em> modified.</p>
*
* @param block the block
* @param transform the transform
* @return the same block
*/
public static <B extends BlockStateHolder<B>> B transform(B block, Transform transform) {
checkNotNull(block);
checkNotNull(transform);
B result = block;
List<? extends Property<?>> properties = block.getBlockType().getProperties();
for (Property<?> property : properties) {
if (property instanceof DirectionalProperty) {
DirectionalProperty dirProp = (DirectionalProperty) property;
Direction value = (Direction) block.getState(property);
if (value != null) {
Vector3 newValue = getNewStateValue(dirProp.getValues(), transform, value.toVector());
if (newValue != null) {
result = result.with(dirProp, Direction.findClosest(newValue, Direction.Flag.ALL));
}
}
} else if (property instanceof EnumProperty) {
EnumProperty enumProp = (EnumProperty) property;
if (property.getName().equals("axis")) {
// We have an axis - this is something we can do the rotations to :sunglasses:
Direction value = null;
switch ((String) block.getState(property)) {
case "x":
value = Direction.EAST;
break;
case "y":
value = Direction.UP;
break;
case "z":
value = Direction.NORTH;
break;
}
if (value != null) {
Vector3 newValue = getNewStateValue(Direction.valuesOf(Direction.Flag.UPRIGHT | Direction.Flag.CARDINAL), transform, value.toVector());
if (newValue != null) {
String axis = null;
Direction newDir = Direction.findClosest(newValue, Direction.Flag.UPRIGHT | Direction.Flag.CARDINAL);
if (newDir == Direction.NORTH || newDir == Direction.SOUTH) {
axis = "z";
} else if (newDir == Direction.EAST || newDir == Direction.WEST) {
axis = "x";
} else if (newDir == Direction.UP || newDir == Direction.DOWN) {
axis = "y";
}
if (axis != null) {
result = result.with(enumProp, axis);
}
}
}
}
} else if (property instanceof IntegerProperty) {
IntegerProperty intProp = (IntegerProperty) property;
if (property.getName().equals("rotation")) {
if (intProp.getValues().size() == 16) {
Optional<Direction> direction = Direction.fromRotationIndex(block.getState(intProp));
int horizontalFlags = Direction.Flag.CARDINAL | Direction.Flag.ORDINAL | Direction.Flag.SECONDARY_ORDINAL;
if (direction.isPresent()) {
Vector3 vec = getNewStateValue(Direction.valuesOf(horizontalFlags), transform, direction.get().toVector());
if (vec != null) {
OptionalInt newRotation = Direction.findClosest(vec, horizontalFlags).toRotationIndex();
if (newRotation.isPresent()) {
result = result.with(intProp, newRotation.getAsInt());
}
}
}
}
}
}
}
List<String> directionalProperties = properties.stream()
.filter(prop -> prop instanceof BooleanProperty)
.filter(prop -> directionNames.contains(prop.getName()))
.filter(property -> (Boolean) block.getState(property))
.map(Property::getName)
.map(String::toUpperCase)
.map(Direction::valueOf)
.map(dir -> Direction.findClosest(transform.apply(dir.toVector()), Direction.Flag.CARDINAL))
.filter(Objects::nonNull)
.map(Direction::name)
.map(String::toLowerCase)
.collect(Collectors.toList());
if (directionalProperties.size() > 0) {
for (String directionName : directionNames) {
result = result.with(block.getBlockType().getProperty(directionName), directionalProperties.contains(directionName));
}
}
return result;
}
public final BaseBlock transformFast(BlockState block) {
BaseBlock transformed = transformBlock(block, false).toBaseBlock();
if (block.hasNbtData()) {
CompoundTag tag = block.getNbtData();
if (tag.containsKey("Rot")) {
int rot = tag.asInt("Rot");
Direction direction = Direction.fromRotationIndex(rot).get();
if (direction != null) {
Vector3 applyAbsolute = transform.apply(direction.toVector());
Vector3 applyOrigin = transform.apply(Vector3.ZERO);
Vector3 newAbsolute = Vector3.at(applyAbsolute.getX() - applyOrigin.getX(), applyAbsolute.getY() - applyOrigin.getY(), applyAbsolute.getZ() - applyOrigin.getZ());
Direction newDirection = Direction.findClosest(newAbsolute, Direction.Flag.CARDINAL | Direction.Flag.ORDINAL | Direction.Flag.SECONDARY_ORDINAL);
if (newDirection != null) {
Map<String, Tag> values = ReflectionUtils.getMap(tag.getValue());
values.put("Rot", new ByteTag((byte) newDirection.toRotationIndex().getAsInt()));
}
}
transformed.setNbtData(tag);
}
}
return transformed;
}
public final BaseBlock transformFastInverse(BlockState block) {
BaseBlock transformed = transformBlock(block, true).toBaseBlock();
if (block.hasNbtData()) {
CompoundTag tag = block.getNbtData();
if (tag.containsKey("Rot")) {
int rot = tag.asInt("Rot");
Direction direction = Direction.fromRotationIndex(rot).get();
if (direction != null) {
Vector3 applyAbsolute = getTransform().inverse().apply(direction.toVector());
Vector3 applyOrigin = getTransform().inverse().apply(Vector3.ZERO);
Vector3 newAbsolute = Vector3.at(applyAbsolute.getX() - applyOrigin.getX(), applyAbsolute.getY() - applyOrigin.getY(), applyAbsolute.getZ() - applyOrigin.getZ());
Direction newDirection = Direction.findClosest(newAbsolute, Direction.Flag.CARDINAL | Direction.Flag.ORDINAL | Direction.Flag.SECONDARY_ORDINAL);
if (newDirection != null) {
Map<String, Tag> values = ReflectionUtils.getMap(tag.getValue());
values.put("Rot", new ByteTag((byte) newDirection.toRotationIndex().getAsInt()));
}
}
}
transformed.setNbtData(tag);
}
return transformed;
}
/**
* Get the new value with the transformed direction.
*
* @param allowedStates the allowed states
* @param transform the transform
* @param oldDirection the old direction to transform
* @return a new state or null if none could be found
*/
@Nullable
//<<<<<<< HEAD
private static Integer getNewStateIndex(Transform transform, List<Direction> directions, int oldIndex) {
Direction oldDirection = directions.get(oldIndex);
Vector3 oldVector = oldDirection.toVector();
Vector3 newVector = transform.apply(oldVector).subtract(transform.apply(Vector3.ZERO)).normalize();
int newIndex = oldIndex;
double closest = oldVector.normalize().dot(newVector);
//=======
// private static Vector3 getNewStateValue(DirectionalProperty state, Transform transform, Vector3 oldDirection) {
// Vector3 newDirection = transform.apply(oldDirection).subtract(transform.apply(Vector3.ZERO)).normalize();
// Vector3 newValue = null;
// double closest = -2;
//>>>>>>> 399e0ad5... Refactor vector system to be cleaner
private static Vector3 getNewStateValue(List<Direction> allowedStates, Transform transform, Vector3 oldDirection) {
Vector3 newDirection = transform.apply(oldDirection).subtract(transform.apply(Vector3.ZERO)).normalize();
Vector3 newValue = null;
double closest = -2;
boolean found = false;
for (int i = 0; i < directions.size(); i++) {
Direction v = directions.get(i);
double dot = v.toVector().normalize().dot(newVector);
if (dot > closest) {
for (Direction v : allowedStates) {
double dot = v.toVector().normalize().dot(newDirection);
if (dot >= closest) {
closest = dot;
newIndex = i;
newValue = v.toVector();
found = true;
}
}
if (found) {
return newIndex;
return newValue;
} else {
return null;
}
}
private void cache() {
BLOCK_ROTATION_BITMASK = new int[BlockTypes.size()];
BLOCK_TRANSFORM = new int[BlockTypes.size()][];
BLOCK_TRANSFORM_INVERSE = new int[BlockTypes.size()][];
outer:
for (int i = 0; i < BLOCK_TRANSFORM.length; i++) {
BLOCK_TRANSFORM[i] = ALL;
BLOCK_TRANSFORM_INVERSE[i] = ALL;
BlockTypes type = BlockTypes.get(i);
int bitMask = 0;
for (AbstractProperty property : (Collection<AbstractProperty>) type.getProperties()) {
Collection<Direction> directions = getDirections(property);
if (directions != null) {
BLOCK_TRANSFORM[i] = null;
BLOCK_TRANSFORM_INVERSE[i] = null;
bitMask |= property.getBitMask();
}
}
if (bitMask != 0) {
BLOCK_ROTATION_BITMASK[i] = bitMask;
}
}
}
@Override
public ResettableExtent setExtent(Extent extent) {
return super.setExtent(extent);
}
public Transform getTransform() {
return transform;
}
public void setTransform(Transform affine) {
this.transform = affine;
this.transformInverse = this.transform.inverse();
cache();
}
private final BlockState transform(BlockState state, int[][] transformArray, Transform transform) {
int typeId = state.getInternalBlockTypeId();
int[] arr = transformArray[typeId];
if (arr == ALL) return state;
if (arr == null) {
arr = transformArray[typeId] = new int[state.getBlockType().getMaxStateId() + 1];
Arrays.fill(arr, -1);
}
int mask = BLOCK_ROTATION_BITMASK[typeId];
int internalId = state.getInternalId();
int maskedId = internalId & mask;
int newMaskedId = arr[maskedId];
if (newMaskedId != -1) {
return BlockState.getFromInternalId(newMaskedId | (internalId & (~mask)));
}
newMaskedId = state.getInternalId();
BlockTypes type = state.getBlockType();
for (AbstractProperty property : (Collection<AbstractProperty>) type.getProperties()) {
List<Direction> directions = getDirections(property);
if (directions != null) {
Integer newIndex = getNewStateIndex(transform, directions, property.getIndex(state.getInternalId()));
if (newIndex != null) {
newMaskedId = property.modifyIndex(newMaskedId, newIndex);
}
}
}
arr[maskedId] = newMaskedId & mask;
return BlockState.getFromInternalId(newMaskedId);
}
public final BlockState transformFast(BlockState block) {
BlockState transformed = transform(block, BLOCK_TRANSFORM, transform);
if (block.hasNbtData()) {
CompoundTag tag = block.getNbtData();
if (tag.containsKey("Rot")) {
int rot = tag.asInt("Rot");
Direction direction = MCDirections.fromRotation(rot);
if (direction != null) {
Vector3 applyAbsolute = transform.apply(direction.toVector());
Vector3 applyOrigin = transform.apply(Vector3.ZERO);
Direction newDirection = Direction.findClosest(applyAbsolute.subtract(applyOrigin), Direction.Flag.CARDINAL | Direction.Flag.ORDINAL | Direction.Flag.SECONDARY_ORDINAL);
if (newDirection != null) {
Map<String, Tag> values = ReflectionUtils.getMap(tag.getValue());
values.put("Rot", new ByteTag((byte) MCDirections.toRotation(newDirection)));
}
}
transformed = new BaseBlock(transformed, tag);
}
}
return transformed;
}
public final BlockState transformFastInverse(BlockState block) {
BlockState transformed = transform(block, BLOCK_TRANSFORM_INVERSE, transformInverse);
if (block.hasNbtData()) {
CompoundTag tag = block.getNbtData();
if (tag.containsKey("Rot")) {
int rot = tag.asInt("Rot");
Direction direction = MCDirections.fromRotation(rot);
if (direction != null) {
Vector3 applyAbsolute = transformInverse.apply(direction.toVector());
Vector3 applyOrigin = transformInverse.apply(Vector3.ZERO);
Direction newDirection = Direction.findClosest(applyAbsolute.subtract(applyOrigin), Direction.Flag.CARDINAL | Direction.Flag.ORDINAL | Direction.Flag.SECONDARY_ORDINAL);
if (newDirection != null) {
Map<String, Tag> values = ReflectionUtils.getMap(tag.getValue());
values.put("Rot", new ByteTag((byte) MCDirections.toRotation(newDirection)));
}
}
}
transformed = new BaseBlock(transformed, tag);
}
return transformed;
}
@Override
public BlockState getLazyBlock(int x, int y, int z) {
return transformFast(super.getLazyBlock(x, y, z));
}
@Override
public BlockState getLazyBlock(BlockVector3 position) {
return transformFast(super.getLazyBlock(position));
}
@Override
public BlockState getBlock(BlockVector3 position) {
return transformFast(super.getBlock(position));
}
@Override
public BaseBiome getBiome(BlockVector2 position) {
return super.getBiome(position);
}
@Override
public boolean setBlock(int x, int y, int z, BlockStateHolder block) throws WorldEditException {
return super.setBlock(x, y, z, transformFastInverse((BlockState) block));
}
@Override
public boolean setBlock(BlockVector3 location, BlockStateHolder block) throws WorldEditException {
return super.setBlock(location, transformFastInverse((BlockState) block));
}
}