Various major

Implement virtual worlds
New schematic commands:
//schem show <directory>
//schem delete * - Deletes the files associated with your currently
loaded schematic
//schem move <directory> - Moves your currently loaded schematics to
this directory
//download -> supports downloading multiple schematics at once
This commit is contained in:
Jesse Boyd 2018-04-13 18:40:42 +10:00
parent b1f8bbddfa
commit 6968e66ad8
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
41 changed files with 1666 additions and 461 deletions

View File

@ -167,15 +167,21 @@ public class FaweBukkit implements IFawe, Listener {
return null;
}
@Override
public void registerPacketListener() {
PluginManager manager = Bukkit.getPluginManager();
if (packetListener == null && manager.getPlugin("ProtocolLib") != null) {
packetListener = new CFIPacketListener(plugin);
}
}
@Override
public synchronized ImageViewer getImageViewer(FawePlayer fp) {
if (listeningImages && imageListener == null) return null;
try {
listeningImages = true;
registerPacketListener();
PluginManager manager = Bukkit.getPluginManager();
if (manager.getPlugin("ProtocolLib") != null) {
packetListener = new CFIPacketListener(plugin);
}
if (manager.getPlugin("PacketListenerApi") == null) {
File output = new File(plugin.getDataFolder().getParentFile(), "PacketListenerAPI_v3.6.0-SNAPSHOT.jar");

View File

@ -2,11 +2,11 @@ package com.boydti.fawe.bukkit.listener;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.command.CFICommands;
import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RunnableVal3;
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
import com.boydti.fawe.util.SetQueue;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
@ -42,7 +42,7 @@ import org.bukkit.inventory.PlayerInventory;
import org.bukkit.plugin.Plugin;
/**
* The CFIPacketListener handles packets for editing the HeightMapMCAGenerator
* The CFIPacketListener handles packets for editing the VirtualWorld
* The generator is a virtual world which only the creator can see
* - The virtual world is displayed inside the current world
* - Block/Chunk/Movement packets need to be handled properly
@ -57,9 +57,9 @@ public class CFIPacketListener implements Listener {
this.protocolmanager = ProtocolLibrary.getProtocolManager();
// Direct digging to the virtual world
registerBlockEvent(PacketType.Play.Client.BLOCK_DIG, false, new RunnableVal3<PacketEvent, HeightMapMCAGenerator, Vector>() {
registerBlockEvent(PacketType.Play.Client.BLOCK_DIG, false, new RunnableVal3<PacketEvent, VirtualWorld, Vector>() {
@Override
public void run(PacketEvent event, HeightMapMCAGenerator gen, Vector pt) {
public void run(PacketEvent event, VirtualWorld gen, Vector pt) {
try {
Player plr = event.getPlayer();
Vector realPos = pt.add(gen.getOrigin());
@ -73,9 +73,9 @@ public class CFIPacketListener implements Listener {
});
// Direct placing to the virtual world
RunnableVal3<PacketEvent, HeightMapMCAGenerator, Vector> placeTask = new RunnableVal3<PacketEvent, HeightMapMCAGenerator, Vector>() {
RunnableVal3<PacketEvent, VirtualWorld, Vector> placeTask = new RunnableVal3<PacketEvent, VirtualWorld, Vector>() {
@Override
public void run(PacketEvent event, HeightMapMCAGenerator gen, Vector pt) {
public void run(PacketEvent event, VirtualWorld gen, Vector pt) {
try {
Player plr = event.getPlayer();
List<EnumWrappers.Hand> hands = event.getPacket().getHands().getValues();
@ -99,9 +99,9 @@ public class CFIPacketListener implements Listener {
registerBlockEvent(PacketType.Play.Client.USE_ITEM, true, placeTask);
// Cancel block change packets where the real world overlaps with the virtual one
registerBlockEvent(PacketType.Play.Server.BLOCK_CHANGE, false, new RunnableVal3<PacketEvent, HeightMapMCAGenerator, Vector>() {
registerBlockEvent(PacketType.Play.Server.BLOCK_CHANGE, false, new RunnableVal3<PacketEvent, VirtualWorld, Vector>() {
@Override
public void run(PacketEvent event, HeightMapMCAGenerator gen, Vector pt) {
public void run(PacketEvent event, VirtualWorld gen, Vector pt) {
// Do nothing
}
});
@ -112,7 +112,7 @@ public class CFIPacketListener implements Listener {
public void onPacketSending(PacketEvent event) {
if (!event.isServerPacket()) return;
HeightMapMCAGenerator gen = getGenerator(event);
VirtualWorld gen = getGenerator(event);
if (gen != null) {
Vector origin = gen.getOrigin();
PacketContainer packet = event.getPacket();
@ -123,7 +123,7 @@ public class CFIPacketListener implements Listener {
int ocx = origin.getBlockX() >> 4;
int ocz = origin.getBlockZ() >> 4;
if (gen.contain(new Vector((cx - ocx) << 4, 0, (cz - ocz) << 4))) {
if (gen.contains(new Vector((cx - ocx) << 4, 0, (cz - ocz) << 4))) {
event.setCancelled(true);
Player plr = event.getPlayer();
@ -147,7 +147,7 @@ public class CFIPacketListener implements Listener {
Player player = event.getPlayer();
Location pos = player.getLocation();
HeightMapMCAGenerator gen = getGenerator(event);
VirtualWorld gen = getGenerator(event);
if (gen != null) {
Vector origin = gen.getOrigin();
Vector pt = new Vector(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
@ -158,7 +158,7 @@ public class CFIPacketListener implements Listener {
int my = ints.read(2);
int mz = ints.read(3);
if (gen.contain(pt.subtract(origin)) && mx == 0 && my == 0 && mz == 0) {
if (gen.contains(pt.subtract(origin)) && mx == 0 && my == 0 && mz == 0) {
event.setCancelled(true);
}
}
@ -172,7 +172,7 @@ public class CFIPacketListener implements Listener {
Player player = event.getPlayer();
Location pos = player.getLocation();
HeightMapMCAGenerator gen = getGenerator(event);
VirtualWorld gen = getGenerator(event);
if (gen != null) {
Vector origin = gen.getOrigin();
Vector from = new Vector(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
@ -180,7 +180,7 @@ public class CFIPacketListener implements Listener {
PacketContainer packet = event.getPacket();
StructureModifier<Double> doubles = packet.getDoubles();
Vector to = new Vector(doubles.read(0), doubles.read(1), doubles.read(2));
if (gen.contain(to.subtract(origin)) && from.distanceSq(to) < 8) {
if (gen.contains(to.subtract(origin)) && from.distanceSq(to) < 8) {
int id = packet.getIntegers().read(0);
PacketContainer reply = new PacketContainer(PacketType.Play.Client.TELEPORT_ACCEPT);
reply.getIntegers().write(0, id);
@ -202,14 +202,14 @@ public class CFIPacketListener implements Listener {
public void onPacketSending(PacketEvent event) {
if (!event.isServerPacket()) return;
HeightMapMCAGenerator gen = getGenerator(event);
VirtualWorld gen = getGenerator(event);
if (gen != null) {
PacketContainer packet = event.getPacket();
ChunkCoordIntPair chunk = packet.getChunkCoordIntPairs().read(0);
Vector origin = gen.getOrigin();
int cx = chunk.getChunkX() - (origin.getBlockX() >> 4);
int cz = chunk.getChunkZ() - (origin.getBlockX() >> 4);
if (gen.contain(new Vector(cx << 4, 0, cz << 4))) {
if (gen.contains(new Vector(cx << 4, 0, cz << 4))) {
event.setCancelled(true);
}
}
@ -220,7 +220,7 @@ public class CFIPacketListener implements Listener {
@EventHandler
public void onTeleport(PlayerTeleportEvent event) {
final Player player = event.getPlayer();
HeightMapMCAGenerator gen = getGenerator(player);
VirtualWorld gen = getGenerator(player);
if (gen != null) {
Location from = event.getFrom();
Location to = event.getTo();
@ -232,7 +232,7 @@ public class CFIPacketListener implements Listener {
}
}
private boolean sendBlockChange(Player plr, HeightMapMCAGenerator gen, Vector pt, Interaction action) {
private boolean sendBlockChange(Player plr, VirtualWorld gen, Vector pt, Interaction action) {
PlatformManager platform = WorldEdit.getInstance().getPlatformManager();
com.sk89q.worldedit.entity.Player actor = FawePlayer.wrap(plr).getPlayer();
com.sk89q.worldedit.util.Location location = new com.sk89q.worldedit.util.Location(actor.getWorld(), pt);
@ -261,19 +261,22 @@ public class CFIPacketListener implements Listener {
}
}
private HeightMapMCAGenerator getGenerator(PacketEvent event) {
private VirtualWorld getGenerator(PacketEvent event) {
return getGenerator(event.getPlayer());
}
private HeightMapMCAGenerator getGenerator(Player player) {
CFICommands.CFISettings settings = FawePlayer.wrap(player).getMeta("CFISettings");
private VirtualWorld getGenerator(Player player) {
FawePlayer<Object> fp = FawePlayer.wrap(player);
VirtualWorld vw = fp.getSession().getVirtualWorld();
if (vw != null) return vw;
CFICommands.CFISettings settings = fp.getMeta("CFISettings");
if (settings != null && settings.hasGenerator() && settings.getGenerator().hasPacketViewer()) {
return settings.getGenerator();
}
return null;
}
private Vector getRelPos(PacketEvent event, HeightMapMCAGenerator generator) {
private Vector getRelPos(PacketEvent event, VirtualWorld generator) {
PacketContainer packet = event.getPacket();
StructureModifier<BlockPosition> position = packet.getBlockPositionModifier();
BlockPosition loc = position.readSafely(0);
@ -283,13 +286,13 @@ public class CFIPacketListener implements Listener {
return pt;
}
private void handleBlockEvent(PacketEvent event, boolean relative, RunnableVal3<PacketEvent, HeightMapMCAGenerator, Vector> task) {
HeightMapMCAGenerator gen = getGenerator(event);
private void handleBlockEvent(PacketEvent event, boolean relative, RunnableVal3<PacketEvent, VirtualWorld, Vector> task) {
VirtualWorld gen = getGenerator(event);
if (gen != null) {
Vector pt = getRelPos(event, gen);
if (pt != null) {
if (relative) pt = getRelative(event, pt);
if (gen.contain(pt)) {
if (gen.contains(pt)) {
event.setCancelled(true);
task.run(event, gen, pt);
}
@ -297,7 +300,7 @@ public class CFIPacketListener implements Listener {
}
}
private void registerBlockEvent(PacketType type, boolean relative, RunnableVal3<PacketEvent, HeightMapMCAGenerator, Vector> task) {
private void registerBlockEvent(PacketType type, boolean relative, RunnableVal3<PacketEvent, VirtualWorld, Vector> task) {
protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, type) {
@Override
public void onPacketReceiving(final PacketEvent event) {

View File

@ -15,11 +15,7 @@ import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.brush.visualization.VisualChunk;
import com.boydti.fawe.object.queue.LazyFaweChunk;
import com.boydti.fawe.object.visitor.FaweChunkVisitor;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.util.*;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
@ -36,49 +32,9 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.atomic.LongAdder;
import net.minecraft.server.v1_12_R1.BiomeBase;
import net.minecraft.server.v1_12_R1.BiomeCache;
import net.minecraft.server.v1_12_R1.Block;
import net.minecraft.server.v1_12_R1.BlockPosition;
import net.minecraft.server.v1_12_R1.ChunkProviderGenerate;
import net.minecraft.server.v1_12_R1.ChunkProviderServer;
import net.minecraft.server.v1_12_R1.ChunkSection;
import net.minecraft.server.v1_12_R1.DataPaletteBlock;
import net.minecraft.server.v1_12_R1.Entity;
import net.minecraft.server.v1_12_R1.EntityPlayer;
import net.minecraft.server.v1_12_R1.EntityTracker;
import net.minecraft.server.v1_12_R1.EntityTypes;
import net.minecraft.server.v1_12_R1.EnumDifficulty;
import net.minecraft.server.v1_12_R1.EnumGamemode;
import net.minecraft.server.v1_12_R1.EnumSkyBlock;
import net.minecraft.server.v1_12_R1.IBlockData;
import net.minecraft.server.v1_12_R1.IDataManager;
import net.minecraft.server.v1_12_R1.MinecraftServer;
import net.minecraft.server.v1_12_R1.NBTTagCompound;
import net.minecraft.server.v1_12_R1.NibbleArray;
import net.minecraft.server.v1_12_R1.PacketDataSerializer;
import net.minecraft.server.v1_12_R1.PacketPlayOutMapChunk;
import net.minecraft.server.v1_12_R1.PacketPlayOutMultiBlockChange;
import net.minecraft.server.v1_12_R1.PlayerChunk;
import net.minecraft.server.v1_12_R1.PlayerChunkMap;
import net.minecraft.server.v1_12_R1.RegionFile;
import net.minecraft.server.v1_12_R1.RegionFileCache;
import net.minecraft.server.v1_12_R1.ServerNBTManager;
import net.minecraft.server.v1_12_R1.TileEntity;
import net.minecraft.server.v1_12_R1.WorldChunkManager;
import net.minecraft.server.v1_12_R1.WorldData;
import net.minecraft.server.v1_12_R1.WorldManager;
import net.minecraft.server.v1_12_R1.WorldServer;
import net.minecraft.server.v1_12_R1.WorldSettings;
import net.minecraft.server.v1_12_R1.WorldType;
import net.minecraft.server.v1_12_R1.*;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
@ -583,6 +539,7 @@ public class BukkitQueue_1_12 extends BukkitQueue_0<net.minecraft.server.v1_12_R
for (int i = 0; i < players.length; i++) {
CraftPlayer bukkitPlayer = ((CraftPlayer) ((BukkitPlayer) players[i]).parent);
EntityPlayer player = bukkitPlayer.getHandle();
if (playerManager.a(player, chunk.getX(), chunk.getZ())) {
if (packet == null) {
byte[] data;
@ -662,6 +619,15 @@ public class BukkitQueue_1_12 extends BukkitQueue_0<net.minecraft.server.v1_12_R
sendChunk(fc.getX(), fc.getZ(), fc.getBitMask());
}
public void sendPacket(int cx, int cz, Packet packet) {
PlayerChunk chunk = getPlayerChunk(nmsWorld, cx, cz);
if (chunk != null) {
for (EntityPlayer player : chunk.c) {
player.playerConnection.sendPacket(packet);
}
}
}
private PlayerChunk getPlayerChunk(WorldServer w, int cx, int cz) {
PlayerChunkMap chunkMap = w.getPlayerChunkMap();
PlayerChunk playerChunk = chunkMap.getChunk(cx, cz);

View File

@ -13,7 +13,9 @@ commands:
aliases: [fawecancel,/fcancel,/cancel,/fawecancel]
permissions:
fawe.plotsquared:
default: true
default: true
children:
fawe.plotsquared.trusted: true
fawe.plotme:
default: true
fawe.bypass:

View File

@ -628,30 +628,32 @@ public class Fawe {
debug(" - https://discord.gg/ngZCzbU");
debug("=======================================");
}
try {
com.github.luben.zstd.util.Native.load();
} catch (Throwable e) {
if (Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL > 6 || Settings.IMP.HISTORY.COMPRESSION_LEVEL > 6) {
Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL = Math.min(6, Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL);
Settings.IMP.HISTORY.COMPRESSION_LEVEL = Math.min(6, Settings.IMP.HISTORY.COMPRESSION_LEVEL);
debug("====== ZSTD COMPRESSION BINDING NOT FOUND ======");
if (!Settings.IMP.EXPERIMENTAL.DISABLE_NATIVES) {
try {
com.github.luben.zstd.util.Native.load();
} catch (Throwable e) {
if (Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL > 6 || Settings.IMP.HISTORY.COMPRESSION_LEVEL > 6) {
Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL = Math.min(6, Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL);
Settings.IMP.HISTORY.COMPRESSION_LEVEL = Math.min(6, Settings.IMP.HISTORY.COMPRESSION_LEVEL);
debug("====== ZSTD COMPRESSION BINDING NOT FOUND ======");
debug(e);
debug("===============================================");
debug("FAWE will work but won't compress data as much");
debug("===============================================");
}
}
try {
net.jpountz.util.Native.load();
} catch (Throwable e) {
debug("====== LZ4 COMPRESSION BINDING NOT FOUND ======");
debug(e);
debug("===============================================");
debug("FAWE will work but won't compress data as much");
debug("FAWE will work but compression will be slower");
debug(" - Try updating your JVM / OS");
debug(" - Report this issue if you cannot resolve it");
debug("===============================================");
}
}
try {
net.jpountz.util.Native.load();
} catch (Throwable e) {
debug("====== LZ4 COMPRESSION BINDING NOT FOUND ======");
debug(e);
debug("===============================================");
debug("FAWE will work but compression will be slower");
debug(" - Try updating your JVM / OS");
debug(" - Report this issue if you cannot resolve it");
debug("===============================================");
}
try {
String arch = System.getenv("PROCESSOR_ARCHITECTURE");
String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432");

View File

@ -40,6 +40,8 @@ public interface IFawe {
default ImageViewer getImageViewer(FawePlayer player) { return null; }
public default void registerPacketListener() {}
default int getPlayerCount() {
return Fawe.get().getCachedPlayers().size();
}

View File

@ -8,12 +8,7 @@ import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.clipboard.MultiClipboardHolder;
import com.boydti.fawe.util.CleanTextureUtil;
import com.boydti.fawe.util.FilteredTextureUtil;
import com.boydti.fawe.util.ImgurUtility;
import com.boydti.fawe.util.StringMan;
import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.util.TextureUtil;
import com.boydti.fawe.util.*;
import com.boydti.fawe.util.chat.Message;
import com.boydti.fawe.util.image.ImageUtil;
import com.intellectualcrafters.plot.PS;
@ -33,11 +28,8 @@ import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandPermissions;
import com.sk89q.worldedit.EmptyClipboardException;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.command.MethodCommands;
import com.sk89q.worldedit.entity.Player;
@ -63,10 +55,9 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayDeque;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.imageio.ImageIO;
@Command(aliases = {"/cfi"}, desc = "Create a world from images: [More Info](https://git.io/v5iDy)")
@ -95,7 +86,7 @@ public class CFICommands extends MethodCommands {
)
@CommandPermissions("worldedit.anvil.cfi")
public void heightmap(FawePlayer fp, BufferedImage image) {
HeightMapMCAGenerator generator = new HeightMapMCAGenerator(image, getFolder("CFI-" + UUID.randomUUID()));
HeightMapMCAGenerator generator = new HeightMapMCAGenerator(image, getFolder(generateName()));
setup(generator, fp);
}
@ -106,14 +97,20 @@ public class CFICommands extends MethodCommands {
)
@CommandPermissions("worldedit.anvil.cfi")
public void heightmap(FawePlayer fp, int width, int length) {
HeightMapMCAGenerator generator = new HeightMapMCAGenerator(width, length, getFolder("CFI-" + UUID.randomUUID()));
HeightMapMCAGenerator generator = new HeightMapMCAGenerator(width, length, getFolder(generateName()));
setup(generator, fp);
}
private String generateName() {
DateFormat df = new SimpleDateFormat("dd.MM.yyyy HH.mm.ss");
String data = df.format(new Date());
return data;
}
private void setup(HeightMapMCAGenerator generator, FawePlayer fp) {
CFISettings settings = getSettings(fp);
settings.remove().setGenerator(generator).bind();
CFISettings settings = getSettings(fp).remove();
generator.setPacketViewer(fp);
settings.setGenerator(generator).bind();
generator.setImageViewer(Fawe.imp().getImageViewer(fp));
generator.update();
mainMenu(fp);
@ -1021,6 +1018,8 @@ public class CFICommands extends MethodCommands {
protected String category;
private boolean bound;
public CFISettings(FawePlayer player) {
this.fp = player;
}
@ -1078,10 +1077,13 @@ public class CFICommands extends MethodCommands {
public CFISettings setGenerator(HeightMapMCAGenerator generator) {
this.generator = generator;
if (bound) fp.getSession().setVirtualWorld(generator);
return this;
}
public CFISettings bind() {
if (generator != null) fp.getSession().setVirtualWorld(generator);
bound = true;
fp.setMeta("CFISettings", this);
return this;
}
@ -1099,11 +1101,10 @@ public class CFICommands extends MethodCommands {
fp.deleteMeta("CFISettings");
HeightMapMCAGenerator gen = this.generator;
if (gen != null) {
gen.close();
LocalSession session = fp.getSession();
session.clearHistory();
fp.getSession().setVirtualWorld(null);
}
popMessages(fp);
bound = false;
generator = null;
image = null;
imageArg = null;

View File

@ -4,6 +4,7 @@ import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FaweCommand;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
import com.boydti.fawe.util.SetQueue;
import com.sk89q.worldedit.EditSession;
import java.util.Collection;
@ -35,6 +36,10 @@ public class Cancel extends FaweCommand {
}
}
}
VirtualWorld world = player.getSession().getVirtualWorld();
if (world != null) {
world.clear();
}
BBC.WORLDEDIT_CANCEL_COUNT.send(player, cancelled);
return true;
}

View File

@ -185,7 +185,15 @@ public enum BBC {
NOTHING_CONFIRMED("You have no actions pending confirmation.", "WorldEdit.Utility"),
SCHEMATIC_PROMPT_CLEAR("&7You may want to use &c%s0 &7to clear your current clipboard first", "Worldedit.Schematic"),
SCHEMATIC_SHOW("&7Displaying &a%s0&7 schematics from &a%s1&7:\n" +
"&8 - &aLeft click &7a structure to set your clipboard\n" +
"&8 - &aRight click &7to add a structure to your multi-clipboard\n" +
"&8 - &7Use &a%s2&7 to go back to the world", "Worldedit.Schematic"),
SCHEMATIC_FORMAT("Available formats (Name: Lookup names)", "Worldedit.Schematic"),
SCHEMATIC_MOVE_EXISTS("&c%s0 already exists", "Worldedit.Schematic"),
SCHEMATIC_MOVE_SUCCESS("&a%s0 -> %s1", "Worldedit.Schematic"),
SCHEMATIC_MOVE_FAILED("&a%s0 no moved: %s1", "Worldedit.Schematic"),
SCHEMATIC_LOADED("%s0 loaded. Paste it with //paste", "Worldedit.Schematic"),
SCHEMATIC_SAVED("%s0 saved.", "Worldedit.Schematic"),
SCHEMATIC_PAGE("Page must be %s", "WorldEdit.Schematic"),

View File

@ -72,7 +72,10 @@ public class Settings extends Config {
public String TEXTURES = "textures";
public String HEIGHTMAP = "heightmap";
public String HISTORY = "history";
@Comment("Multiple servers can use the same clipboards")
@Comment({
"Multiple servers can use the same clipboards",
" - Use a shared directory or NFS/Samba"
})
public String CLIPBOARD = "clipboard";
@Comment("Each player has their own sub directory for schematics")
public boolean PER_PLAYER_SCHEMATICS = true;
@ -315,6 +318,11 @@ public class Settings extends Config {
})
public boolean VANILLA_CUI = false;
@Comment({
"Disable using native libraries",
})
public boolean DISABLE_NATIVES = false;
}
public static class WEB {

View File

@ -3,41 +3,20 @@ package com.boydti.fawe.jnbt.anvil;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.example.SimpleCharFaweChunk;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FaweInputStream;
import com.boydti.fawe.object.FaweLocation;
import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.Metadatable;
import com.boydti.fawe.object.PseudoRandom;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.object.*;
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
import com.boydti.fawe.object.change.StreamChange;
import com.boydti.fawe.object.changeset.CFIChangeSet;
import com.boydti.fawe.object.collection.DifferentialArray;
import com.boydti.fawe.object.collection.DifferentialBlockBuffer;
import com.boydti.fawe.object.collection.IterableThreadLocal;
import com.boydti.fawe.object.collection.LocalBlockVector2DSet;
import com.boydti.fawe.object.collection.SummedAreaTable;
import com.boydti.fawe.object.collection.*;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.object.queue.LazyFaweChunk;
import com.boydti.fawe.object.schematic.Schematic;
import com.boydti.fawe.util.CachedTextureUtil;
import com.boydti.fawe.util.RandomTextureUtil;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.util.TextureUtil;
import com.boydti.fawe.util.*;
import com.boydti.fawe.util.image.Drawable;
import com.boydti.fawe.util.image.ImageViewer;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.blocks.BlockID;
@ -51,24 +30,18 @@ import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.world.SimpleWorld;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BaseBiome;
import com.sk89q.worldedit.world.registry.WorldData;
import java.awt.image.BufferedImage;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.*;
import javax.annotation.Nullable;
public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, FaweQueue, StreamChange, Closeable, Drawable {
public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Drawable, VirtualWorld {
private final MutableBlockVector mutable = new MutableBlockVector();
private final ThreadLocal<int[]> indexStore = new ThreadLocal<int[]>() {
@ -251,6 +224,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw
throw new UnsupportedOperationException("Not supported: Queue is not backed by a real world");
}
@Override
public Vector getOrigin() {
return new BlockVector(chunkOffset.getBlockX() << 4, 0, chunkOffset.getBlockZ() << 4);
}
@ -286,6 +260,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw
return viewer;
}
@Override
public void update() {
if (viewer != null) {
viewer.view(this);
@ -326,9 +301,6 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw
e.printStackTrace();
}
});
// FaweChunk toSend = getSnapshot(chunk, finalCX, finalCZ);
// toSend.setLoc(HeightMapMCAGenerator.this, finalCX + OX, finalCZ + OZ);
// packetQueue.sendChunkUpdate(toSend, player);
}
}
}
@ -695,6 +667,11 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw
return new Vector(0, 0, 0);
}
@Override
public FawePlayer getPlayer() {
return player;
}
@Override
public Vector getMaximumPoint() {
return new Vector(getWidth() - 1, 255, getLength() - 1);
@ -774,6 +751,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw
return new SimpleCharFaweChunk(this, chunkX, chunkZ);
}
@Override
public FaweChunk getSnapshot(int chunkX, int chunkZ) {
return getSnapshot(null, chunkX, chunkZ);
}
@ -899,9 +877,9 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw
}
@Override
public void close() {
public void close(boolean update) {
clear();
if (chunkOffset != null && player != null) {
if (chunkOffset != null && player != null && update) {
FaweQueue packetQueue = SetQueue.IMP.getNewQueue(player.getWorld(), true, false);
int lenCX = (getWidth() + 15) >> 4;
@ -925,6 +903,11 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw
}
}
}
if (player != null) {
player.deleteMeta("CFISettings");
LocalSession session = player.getSession();
session.clearHistory();
}
player = null;
chunkOffset = null;
}
@ -2151,7 +2134,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw
@Override
public int getMaxY() {
return SimpleWorld.super.getMaxY();
return 255;
}
@Override

View File

@ -11,24 +11,11 @@ import com.boydti.fawe.util.ArrayUtil;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.NBTConstants;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.jnbt.Tag;
import com.sk89q.jnbt.*;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.*;
public class MCAChunk extends FaweChunk<Void> {
@ -102,13 +89,7 @@ public class MCAChunk extends FaweChunk<Void> {
}
}
public byte[] toBytes(byte[] buffer) throws IOException {
if (buffer == null) {
buffer = new byte[8192];
}
FastByteArrayOutputStream buffered = new FastByteArrayOutputStream(buffer);
DataOutputStream dataOut = new DataOutputStream(buffered);
NBTOutputStream nbtOut = new NBTOutputStream((DataOutput) dataOut);
public void write(NBTOutputStream nbtOut) throws IOException {
nbtOut.writeNamedTagName("", NBTConstants.TYPE_COMPOUND);
nbtOut.writeLazyCompoundTag("Level", new NBTOutputStream.LazyWrite() {
@Override
@ -135,12 +116,12 @@ public class MCAChunk extends FaweChunk<Void> {
}
out.writeNamedTag("HeightMap", heightMap);
out.writeNamedTagName("Sections", NBTConstants.TYPE_LIST);
dataOut.writeByte(NBTConstants.TYPE_COMPOUND);
nbtOut.getOutputStream().writeByte(NBTConstants.TYPE_COMPOUND);
int len = 0;
for (int layer = 0; layer < ids.length; layer++) {
if (ids[layer] != null) len++;
}
dataOut.writeInt(len);
nbtOut.getOutputStream().writeInt(len);
for (int layer = 0; layer < ids.length; layer++) {
byte[] idLayer = ids[layer];
if (idLayer == null) {
@ -156,7 +137,17 @@ public class MCAChunk extends FaweChunk<Void> {
}
});
nbtOut.writeEndTag();
nbtOut.close();
}
public byte[] toBytes(byte[] buffer) throws IOException {
if (buffer == null) {
buffer = new byte[8192];
}
FastByteArrayOutputStream buffered = new FastByteArrayOutputStream(buffer);
DataOutputStream dataOut = new DataOutputStream(buffered);
try (NBTOutputStream nbtOut = new NBTOutputStream((DataOutput) dataOut)) {
write(nbtOut);
}
return buffered.toByteArray();
}
@ -465,7 +456,7 @@ public class MCAChunk extends FaweChunk<Void> {
return FaweCache.asTag(root);
}
public MCAChunk(NBTInputStream nis, FaweQueue parent, int x, int z, int compressedSize) throws IOException {
public MCAChunk(NBTInputStream nis, FaweQueue parent, int x, int z, boolean readPos) throws IOException {
super(parent, x, z);
ids = new byte[16][];
data = new byte[16][];
@ -527,6 +518,20 @@ public class MCAChunk extends FaweChunk<Void> {
heightMap = value;
}
});
if (readPos) {
streamer.addReader(".Level.xPos", new RunnableVal2<Integer, Integer>() {
@Override
public void run(Integer index, Integer value) {
MCAChunk.this.setLoc(getParent(), value, getZ());
}
});
streamer.addReader(".Level.zPos", new RunnableVal2<Integer, Integer>() {
@Override
public void run(Integer index, Integer value) {
MCAChunk.this.setLoc(getParent(), getX(), value);
}
});
}
streamer.readFully();
}

View File

@ -86,6 +86,17 @@ public class MCAFile {
Z = Integer.parseInt(split[2]);
}
public MCAFile(FaweQueue parent, int mcrX, int mcrZ) throws Exception {
this(parent, mcrX, mcrZ, new File(parent.getSaveFolder(), "r." + mcrX + "." + mcrZ + ".mca"));
}
public MCAFile(FaweQueue parent, int mcrX, int mcrZ, File file) {
this.queue = parent;
this.file = file;
X = mcrX;
Z = mcrZ;
}
public void clear() {
if (raf != null) {
try {
@ -130,12 +141,14 @@ public class MCAFile {
try {
if (raf == null) {
this.locations = new byte[4096];
this.raf = new RandomAccessFile(file, "rw");
if (raf.length() < 8192) {
raf.setLength(8192);
} else {
raf.seek(0);
raf.readFully(locations);
if (file != null) {
this.raf = new RandomAccessFile(file, "rw");
if (raf.length() < 8192) {
raf.setLength(8192);
} else {
raf.seek(0);
raf.readFully(locations);
}
}
}
} catch (Throwable e) {
@ -143,10 +156,6 @@ public class MCAFile {
}
}
public MCAFile(FaweQueue parent, int mcrX, int mcrZ) throws Exception {
this(parent, new File(parent.getSaveFolder(), "r." + mcrX + "." + mcrZ + ".mca"));
}
public int getX() {
return X;
}
@ -196,7 +205,7 @@ public class MCAFile {
return null;
}
NBTInputStream nis = getChunkIS(offset);
MCAChunk chunk = new MCAChunk(nis, queue, cx, cz, size);
MCAChunk chunk = new MCAChunk(nis, queue, cx, cz, false);
nis.close();
int pair = MathMan.pair((short) (cx & 31), (short) (cz & 31));
synchronized (chunks) {

View File

@ -116,38 +116,38 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
int otherBCZ = (obz) >> 4;
int otherTCX = (otx) >> 4;
int otherTCZ = (otz) >> 4;
int cx = newChunk.getX();
int cz = newChunk.getZ();
int cbx = (cx << 4) - oX;
int cbz = (cz << 4) - oZ;
int cx = newChunk.getX();
int cz = newChunk.getZ();
int cbx = (cx << 4) - oX;
int cbz = (cz << 4) - oZ;
boolean changed = false;
boolean changed = false;
for (int otherCZ = otherBCZ; otherCZ <= otherTCZ; otherCZ++) {
for (int otherCX = otherBCX; otherCX <= otherTCX; otherCX++) {
FaweChunk chunk;
synchronized (this) {
chunk = this.getFaweChunk(otherCX, otherCZ);
}
if (!(chunk instanceof NullFaweChunk)) {
changed = true;
MCAChunk other = (MCAChunk) chunk;
int ocbx = otherCX << 4;
int ocbz = otherCZ << 4;
int octx = ocbx + 15;
int octz = ocbz + 15;
int offsetY = 0;
int minX = obx > ocbx ? (obx - ocbx) & 15 : 0;
int maxX = otx < octx ? (otx - ocbx) : 15;
int minZ = obz > ocbz ? (obz - ocbz) & 15 : 0;
int maxZ = otz < octz ? (otz - ocbz) : 15;
int offsetX = ocbx - cbx;
int offsetZ = ocbz - cbz;
newChunk.copyFrom(other, minX, maxX, 0, 255, minZ, maxZ, offsetX, offsetY, offsetZ);
}
for (int otherCX = otherBCX; otherCX <= otherTCX; otherCX++) {
FaweChunk chunk;
synchronized (this) {
chunk = this.getFaweChunk(otherCX, otherCZ);
}
if (!(chunk instanceof NullFaweChunk)) {
changed = true;
MCAChunk other = (MCAChunk) chunk;
int ocbx = otherCX << 4;
int ocbz = otherCZ << 4;
int octx = ocbx + 15;
int octz = ocbz + 15;
int offsetY = 0;
int minX = obx > ocbx ? (obx - ocbx) & 15 : 0;
int maxX = otx < octx ? (otx - ocbx) : 15;
int minZ = obz > ocbz ? (obz - ocbz) & 15 : 0;
int maxZ = otz < octz ? (otz - ocbz) : 15;
int offsetX = ocbx - cbx;
int offsetZ = ocbz - cbz;
newChunk.copyFrom(other, minX, maxX, 0, 255, minZ, maxZ, offsetX, offsetY, offsetZ);
}
}
return changed;
}
return changed;
}
@Override
public boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean save, boolean unload) {

View File

@ -53,13 +53,19 @@ public class MCAQueueMap implements IFaweQueueMap {
if (lastFile == null) {
try {
queue.setMCA(lastFileX, lastFileZ, RegionWrapper.GLOBAL(), null, true, false);
File file = new File(queue.getSaveFolder(), "r." + lastFileX + "." + lastFileZ + ".mca");
if (create) {
File parent = file.getParentFile();
if (!parent.exists()) parent.mkdirs();
if (!file.exists()) file.createNewFile();
File save = queue.getSaveFolder();
File file;
if (save != null) {
file = new File(queue.getSaveFolder(), "r." + lastFileX + "." + lastFileZ + ".mca");
if (create) {
File parent = file.getParentFile();
if (!parent.exists()) parent.mkdirs();
if (!file.exists()) file.createNewFile();
}
} else {
file = null;
}
lastFile = tmp = new MCAFile(queue, file);
lastFile = tmp = new MCAFile(queue, mcaX, mcaZ, file);
} catch (FaweException.FaweChunkLoadException ignore) {
lastFile = null;
return null;

View File

@ -56,7 +56,7 @@ public class DebugFixAir extends MCAFilterCounter {
}
}
ids0[i] = BlockID.BEDROCK;
chunk.ids[4][i] = BlockID.GRASS;
if (chunk.ids[4][i] == 0) chunk.ids[4][i] = BlockID.GRASS;
cache.add(256);
}
}

View File

@ -0,0 +1,16 @@
package com.boydti.fawe.object;
import java.util.function.Consumer;
public abstract class DelegateConsumer<T> implements Consumer<T> {
private final Consumer parent;
public DelegateConsumer(Consumer parent) {
this.parent = parent;
}
@Override
public void accept(T o) {
parent.accept(o);
}
}

View File

@ -1,5 +1,6 @@
package com.boydti.fawe.object;
import com.boydti.fawe.util.IOUtil;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NamedTag;
import java.io.DataInputStream;
@ -64,16 +65,8 @@ public class FaweInputStream extends DataInputStream {
}
}
public int readVarInt() throws IOException {
int i = 0;
int offset = 0;
int b;
while ((b = read()) > 127) {
i |= (b - 128) << offset;
offset += 7;
}
i |= b << offset;
return i;
public final int readVarInt() throws IOException {
return IOUtil.readVarInt(this);
}
@Override

View File

@ -18,7 +18,6 @@ public class FaweLimit {
public boolean FAST_PLACEMENT = false;
public boolean CONFIRM_LARGE = true;
public static FaweLimit MAX;
static {

View File

@ -5,7 +5,7 @@ import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.command.CFICommands;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator;
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.regions.FaweMaskManager;
@ -302,6 +302,13 @@ public abstract class FawePlayer<T> extends Metadatable {
return false;
}
public boolean checkAction() {
long time = getMeta("faweActionTick", Long.MIN_VALUE);
long tick = Fawe.get().getTimer().getTick();
setMeta("faweActionTick", tick);
return tick > time;
}
/**
* Loads any history items from disk:
* - Should already be called if history on disk is enabled
@ -610,16 +617,24 @@ public abstract class FawePlayer<T> extends Metadatable {
return new EditSessionBuilder(getWorld()).player(this).build();
}
public void setVirtualWorld(VirtualWorld world) {
getSession().setVirtualWorld(world);
}
/**
* Get the World the player is editing in (may not match the world they are in)<br/>
* - e.g. If they are editing a CFI world.<br/>
* @return Editing world
*/
public World getWorldForEditing() {
CFICommands.CFISettings cfi = getMeta("CFISettings");
if (cfi != null && cfi.hasGenerator() && cfi.getGenerator().hasPacketViewer()) {
return cfi.getGenerator();
VirtualWorld virtual = getSession().getVirtualWorld();
if (virtual != null) {
return virtual;
}
// CFICommands.CFISettings cfi = getMeta("CFISettings");
// if (cfi != null && cfi.hasGenerator() && cfi.getGenerator().hasPacketViewer()) {
// return cfi.getGenerator();
// }
return WorldEdit.getInstance().getPlatformManager().getWorldForEditing(getWorld());
}
@ -640,8 +655,8 @@ public abstract class FawePlayer<T> extends Metadatable {
}
PlayerProxy proxy = new PlayerProxy(player, permActor, cuiActor, world);
if (world instanceof HeightMapMCAGenerator) {
proxy.setOffset(Vector.ZERO.subtract(((HeightMapMCAGenerator) world).getOrigin()));
if (world instanceof VirtualWorld) {
proxy.setOffset(Vector.ZERO.subtract(((VirtualWorld) world).getOrigin()));
}
return proxy;
}

View File

@ -3,6 +3,7 @@ package com.boydti.fawe.object;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.regions.CuboidRegion;
@Deprecated
public class RegionWrapper extends CuboidRegion {
private final static RegionWrapper GLOBAL = new RegionWrapper(Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);

View File

@ -0,0 +1,265 @@
package com.boydti.fawe.object.brush.visualization;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.example.SimpleCharFaweChunk;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.util.SetQueue;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.UUID;
import javax.annotation.Nullable;
public abstract class ImmutableVirtualWorld implements VirtualWorld {
@Override
public int getMaxY() {
return 255;
}
@Override
public Collection<FaweChunk> getFaweChunks() {
return Collections.emptyList();
}
@Override
public boolean regenerateChunk(int x, int z, @Nullable BaseBiome biome, @Nullable Long seed) {
return unsupported();
}
@Override
public void sendBlockUpdate(FaweChunk chunk, FawePlayer... players) {
}
@Override
public File getSaveFolder() {
return null;
}
@Override
public void addNotifyTask(int x, int z, Runnable runnable) {
if (runnable != null) runnable.run();
}
@Override
public BaseBiome getBiome(Vector2D position) {
return FaweCache.getBiome(0);
}
@Override
public int getCombinedId4Data(int x, int y, int z, int def) {
return getCombinedId4Data(x, y, z);
}
@Override
public int getCachedCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException {
return getCombinedId4Data(x, y, z);
}
@Override
public boolean hasSky() {
return true;
}
@Override
public int getSkyLight(int x, int y, int z) {
return 15;
}
@Override
public int getEmmittedLight(int x, int y, int z) {
return 0;
}
@Override
public CompoundTag getTileEntity(int x, int y, int z) throws FaweException.FaweChunkLoadException {
return null;
}
@Override
public int size() {
return 0;
}
@Override
public void setWorld(String world) {
}
@Override
public World getWEWorld() {
return this;
}
@Override
public String getWorldName() {
return getName();
}
@Override
public long getModified() {
return 0;
}
@Override
public void setModified(long modified) {
// Unsupported
}
@Override
public RunnableVal2<ProgressType, Integer> getProgressTask() {
return null;
}
@Override
public void setProgressTask(RunnableVal2<ProgressType, Integer> progressTask) {
}
@Override
public void setChangeTask(RunnableVal2<FaweChunk, FaweChunk> changeTask) {
}
@Override
public RunnableVal2<FaweChunk, FaweChunk> getChangeTask() {
return null;
}
@Override
public SetQueue.QueueStage getStage() {
return SetQueue.QueueStage.NONE;
}
@Override
public void setStage(SetQueue.QueueStage stage) {
// Not supported
}
@Override
public void addNotifyTask(Runnable runnable) {
runnable.run();
}
@Override
public void runTasks() {
}
@Override
public void addTask(Runnable whenFree) {
whenFree.run();
}
@Override
public boolean isEmpty() {
return true;
}
@Nullable
@Override
public Operation commit() {
return null;
}
@Override
public String getName() {
return Integer.toString(hashCode());
}
@Override
public boolean setBlock(Vector position, BaseBlock block, boolean notifyAndLight) throws WorldEditException {
return setBlock(position, block);
}
@Override
public int getBlockLightLevel(Vector position) {
return 0;
}
@Override
public boolean clearContainerBlockContents(Vector position) {
return unsupported();
}
@Override
public void dropItem(Vector position, BaseItemStack item) {
unsupported();
}
@Override
public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, Vector position) throws MaxChangedBlocksException {
return unsupported();
}
@Override
public FaweChunk getFaweChunk(int chunkX, int chunkZ) {
return new SimpleCharFaweChunk(this, chunkX, chunkZ);
}
@Override
public void setEntity(int x, int y, int z, CompoundTag tag) {
unsupported();
}
@Override
public boolean setBlock(Vector pt, BaseBlock block) throws WorldEditException {
return unsupported();
}
@Override
public boolean setBlock(int x, int y, int z, int id, int data) {
return unsupported();
}
@Override
public void setTile(int x, int y, int z, CompoundTag tag) {
unsupported();
}
@Override
public void removeEntity(int x, int y, int z, UUID uuid) {
unsupported();
}
@Override
public boolean setBiome(int x, int z, BaseBiome biome) {
return unsupported();
}
@Override
public void setChunk(FaweChunk chunk) {
unsupported();
}
@Override
public boolean next(int amount, long time) {
return unsupported();
}
@Override
public boolean regenerate(Region region, EditSession editSession) {
return unsupported();
}
@Override
public void clear() {
// do nothing - world is immutable
}
private boolean unsupported() {
throw new UnsupportedOperationException("World is immutable");
}
}

View File

@ -0,0 +1,51 @@
package com.boydti.fawe.object.brush.visualization;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.event.platform.BlockInteractEvent;
import com.sk89q.worldedit.event.platform.PlayerInputEvent;
import com.sk89q.worldedit.world.SimpleWorld;
import java.io.Closeable;
import java.io.IOException;
public interface VirtualWorld extends SimpleWorld, FaweQueue, Closeable {
Vector getOrigin();
FaweChunk getSnapshot(int chunkX, int chunkZ);
@Override
int getMaxY();
@Override
boolean setBlock(Vector pt, BaseBlock block) throws WorldEditException;
@Override
default Vector getMaximumPoint() {
return FaweQueue.super.getMaximumPoint();
}
@Override
default Vector getMinimumPoint() {
return FaweQueue.super.getMinimumPoint();
}
FawePlayer getPlayer();
void update();
@Override
default void close() throws IOException {
close(true);
}
void close(boolean update) throws IOException;
default void handleBlockInteract(Player player, Vector pos, BlockInteractEvent event) {}
default void handlePlayerInput(Player player, PlayerInputEvent event) {}
}

View File

@ -20,7 +20,7 @@ public class CFIChangeSet extends FaweChangeSet {
public CFIChangeSet(HeightMapMCAGenerator hmmg, UUID uuid) throws IOException {
super(hmmg);
File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY + File.separator + uuid + File.separator + hmmg.getWorldName());
File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY + File.separator + uuid + File.separator + "CFI" + File.separator + hmmg.getWorldName());
int max = MainUtil.getMaxFileId(folder);
this.file = new File(folder, Integer.toString(max) + ".cfi");
File parent = this.file.getParentFile();

View File

@ -6,9 +6,7 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.registry.WorldData;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.*;
import static com.google.common.base.Preconditions.checkNotNull;
@ -139,6 +137,18 @@ public class MultiClipboardHolder extends URIClipboardHolder {
return available[index];
}
@Override
public Set<URI> getURIs() {
Set<URI> set = new HashSet<>();
for (ClipboardHolder holder : getHolders()) {
if (holder instanceof URIClipboardHolder) {
URI uri = ((URIClipboardHolder) holder).getUri();
if (!uri.toString().isEmpty()) set.add(uri);
}
}
return set;
}
@Override
public void close() {
cached = null;

View File

@ -4,6 +4,8 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.registry.WorldData;
import java.net.URI;
import java.util.Collections;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
@ -30,4 +32,8 @@ public class URIClipboardHolder extends ClipboardHolder {
public URI getUri() {
return uri;
}
public Set<URI> getURIs() {
return Collections.singleton(uri);
}
}

View File

@ -0,0 +1,553 @@
package com.boydti.fawe.object.schematic.visualizer;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.jnbt.anvil.MCAChunk;
import com.boydti.fawe.jnbt.anvil.MCAQueue;
import com.boydti.fawe.object.*;
import com.boydti.fawe.object.brush.visualization.ImmutableVirtualWorld;
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.exception.FaweException;
import com.boydti.fawe.object.queue.LazyFaweChunk;
import com.boydti.fawe.object.schematic.Schematic;
import com.boydti.fawe.util.*;
import com.google.common.io.ByteSource;
import com.google.common.io.Files;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BlockID;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.event.platform.PlayerInputEvent;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.TargetBlock;
import com.sk89q.worldedit.world.registry.WorldData;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import java.io.*;
import java.net.URI;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.util.*;
/**
* An Immutable virtual world used to display & select schematics
*/
public class SchemVis extends ImmutableVirtualWorld {
private final WorldData worldData;
private final Long2ObjectOpenHashMap<Map.Entry<File, Long>> files;
private final Long2ObjectOpenHashMap<MCAChunk> chunks; // TODO use soft references OR clear chunks outside view distance
private final MutableBlockVector2D lastPos = new MutableBlockVector2D();
private final FawePlayer player;
private final Location origin;
private final BlockVector2D chunkOffset;
public SchemVis(FawePlayer player) {
this.files = new Long2ObjectOpenHashMap<>();
this.chunks = new Long2ObjectOpenHashMap<>();
this.player = player;
this.worldData = player.getWorld().getWorldData();
// Set the origin to somewhere around where the player currently is
FaweLocation pos = player.getLocation();
this.origin = player.getPlayer().getLocation();
this.chunkOffset = new BlockVector2D(pos.x >> 4,pos.z >> 4);
}
@Override
public void handlePlayerInput(Player player, PlayerInputEvent event) {
int range = 240;
BlockWorldVector target = new TargetBlock(player, range, 0.2).getAnyTargetBlock();
if (target != null) {
int chunkX = target.getBlockX() >> 4;
int chunkZ = target.getBlockZ() >> 4;
long pos = MathMan.pairInt(chunkX, chunkZ);
Map.Entry<File, Long> entry = files.get(pos);
if (entry != null) {
File cachedFile = entry.getKey();
String filename = cachedFile.getName();
// get non `.cached` file
File file = new File(cachedFile.getParentFile(), filename.substring(1, filename.length() - 7));
URI uri = file.toURI();
ClipboardFormat format = ClipboardFormat.findByFile(file);
if (format != null) {
LocalSession session = this.player.getSession();
try {
synchronized (this) {
switch (event.getInputType()) {
case PRIMARY: // set clipboard
format.hold(player, uri, new FileInputStream(file));
BBC.SCHEMATIC_LOADED.send(player, filename);
session.setVirtualWorld(null);
break;
case SECONDARY: // add/remove clipboard
ClipboardHolder existing = session.getExistingClipboard();
boolean contains = existing instanceof URIClipboardHolder && ((URIClipboardHolder) existing).contains(uri);
if (contains) {
// Remove it
if (existing instanceof MultiClipboardHolder) {
MultiClipboardHolder multi = ((MultiClipboardHolder) existing);
multi.remove(uri);
if (multi.getClipboards().isEmpty()) session.setClipboard(null);
} else {
session.setClipboard(null);
}
BBC.CLIPBOARD_CLEARED.send(player);
} else {
// Add it
ByteSource source = Files.asByteSource(file);
MultiClipboardHolder multi = new MultiClipboardHolder(URI.create(""), worldData, new LazyClipboardHolder(uri, source, format, worldData, null));
session.addClipboard(multi);
BBC.SCHEMATIC_LOADED.send(player, filename);
}
// Resend relevant chunks
FaweQueue packetQueue = SetQueue.IMP.getNewQueue(this.player.getWorld(), true, false);
if (packetQueue.supports(Capability.CHUNK_PACKETS)) {
ArrayDeque<Long> toSend = new ArrayDeque<>();
int OX = chunkOffset.getBlockX();
int OZ = chunkOffset.getBlockZ();
ObjectIterator<Long2ObjectMap.Entry<MCAChunk>> iter = chunks.long2ObjectEntrySet().fastIterator();
while (iter.hasNext()) {
Long2ObjectMap.Entry<MCAChunk> mcaChunkEntry = iter.next();
long curChunkPos = mcaChunkEntry.getLongKey();
Map.Entry<File, Long> curFileEntry = files.get(curChunkPos);
if (curFileEntry != null && curFileEntry == entry) {
MCAChunk chunk = mcaChunkEntry.getValue();
if (contains) {
iter.remove();
}
else select(chunk);
toSend.add(curChunkPos);
}
}
for (long curChunkPos : toSend) send(packetQueue, MathMan.unpairIntX(curChunkPos), MathMan.unpairIntY(curChunkPos));
}
break;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
/**
* Discard chunks outside FOV
*/
private void clean() {
if (chunks.size() > 225) {
TaskManager.IMP.sync(() -> {
if (chunks.size() > 225) {
synchronized (SchemVis.this) {
FaweLocation pos = player.getLocation();
int centerX = pos.x >> 4;
int centerZ = pos.z >> 4;
ObjectIterator<Long2ObjectMap.Entry<MCAChunk>> iter = chunks.long2ObjectEntrySet().fastIterator();
while (iter.hasNext()) {
Long2ObjectMap.Entry<MCAChunk> entry = iter.next();
long pair = entry.getLongKey();
int chunkX = MathMan.unpairIntX(pair);
int chunkZ = MathMan.unpairIntY(pair);
if (Math.abs(centerX - chunkX) > 15 || Math.abs(centerZ - chunkZ) > 15) {
iter.remove();
}
}
}
}
return null;
});
}
}
/**
* Send a chunk
* @param packetQueue
* @param chunkX
* @param chunkZ
*/
private void send(FaweQueue packetQueue, int chunkX, int chunkZ) {
TaskManager.IMP.getPublicForkJoinPool().submit(() -> {
try {
int OX = chunkOffset.getBlockX();
int OZ = chunkOffset.getBlockZ();
FaweChunk toSend = getSnapshot(chunkX, chunkZ);
toSend.setLoc(SchemVis.this, chunkX + OX, chunkZ + OZ);
packetQueue.sendChunkUpdate(toSend, SchemVis.this.player);
} catch (Throwable e) {
e.printStackTrace();
}
});
}
/**
* The offset for this virtual world
* @return offset vector
*/
@Override
public Vector getOrigin() {
return new BlockVector(chunkOffset.getBlockX() << 4, 0, chunkOffset.getBlockZ() << 4);
}
/**
* @param chunkX
* @param chunkZ
* @return The schematic file visualized at a chunk position
*/
private File getFile(int chunkX, int chunkZ) {
long pair = MathMan.pairInt(chunkX, chunkZ);
Map.Entry<File, Long> entry = files.get(pair);
return entry != null ? entry.getKey() : null;
}
private Map.Entry<File, Long> getEntry(File file, long position) {
return new AbstractMap.SimpleEntry(file, position);
}
/**
* Replace the blocks with glass, to indicate it's been selected
* @param chunk
*/
private void select(MCAChunk chunk) {
for (int layer = 0; layer < 16; layer++) {
byte[] ids = chunk.ids[layer];
if (ids != null) {
for (int i = 0; i < ids.length; i++) {
if (ids[i] != 0) ids[i] = BlockID.STAINED_GLASS;
Arrays.fill(chunk.data[layer], (byte) 0);
}
}
}
}
/**
* Cache a chunk
* @param file
* @param chunk
*/
private void cacheChunk(File file, MCAChunk chunk, boolean selected) {
long pair = MathMan.pairInt(chunk.getX(), chunk.getZ());
// Light chunk
for (int layer = 0; layer < 16; layer++) {
if (chunk.skyLight[layer] != null) {
Arrays.fill(chunk.skyLight[layer], (byte) 255);
}
}
if (selected) {
select(chunk);
}
synchronized (this) {
chunks.put(pair, chunk);
}
}
/**
* Get the next free position for a schematic of the provided dimensions
* @param schemDimensions
* @return
*/
private BlockVector2D registerAndGetChunkOffset(BlockVector schemDimensions, File file) {
int chunkX = schemDimensions.getBlockX() >> 4;
int chunkZ = schemDimensions.getBlockZ() >> 4;
MutableBlockVector2D pos2 = new MutableBlockVector2D();
MutableBlockVector2D curPos = lastPos;
// Find next free position
while (!isAreaFree(curPos, pos2.setComponents(curPos.getBlockX() + chunkX, curPos.getBlockZ() + chunkZ))) {
if (curPos == lastPos && !files.containsKey(MathMan.pairInt(curPos.getBlockX(), curPos.getBlockZ()))) {
curPos = new MutableBlockVector2D();
curPos.setComponents(lastPos.getBlockX(), lastPos.getBlockZ());
}
curPos.nextPosition();
}
// Register the chunks
Map.Entry<File, Long> originValue = getEntry(file, MathMan.pairInt(curPos.getBlockX(), curPos.getBlockZ()));
for (int x = 0; x <= chunkX; x++) {
for (int z = 0; z <= chunkZ; z++) {
long pos = MathMan.pairInt(curPos.getBlockX() + x, curPos.getBlockZ() + z);
files.put(pos, originValue);
}
}
return curPos;
}
private boolean isAreaFree(BlockVector2D chunkPos1, BlockVector2D chunkPos2 /* inclusive */) {
for (int x = chunkPos1.getBlockX(); x <= chunkPos2.getBlockX(); x++) {
for (int z = chunkPos1.getBlockZ(); z <= chunkPos2.getBlockZ(); z++) {
if (files.containsKey(MathMan.pairInt(x, z)) || (x == 0 && z == 0)) return false;
}
}
return true;
}
private boolean isSelected(File file) {
ClipboardHolder clipboard = player.getSession().getExistingClipboard();
if (clipboard != null) {
if (clipboard instanceof URIClipboardHolder) {
return ((URIClipboardHolder) clipboard).contains(file.toURI());
}
}
return false;
}
public void add(File file) throws IOException {
File cached = new File(file.getParentFile(), "." + file.getName() + ".cached");
if (cached.exists() && file.lastModified() <= cached.lastModified()) {
try (FileInputStream fis = new FileInputStream(cached)) {
BlockVector dimensions = new BlockVector(IOUtil.readVarInt(fis), IOUtil.readVarInt(fis), IOUtil.readVarInt(fis));
BlockVector2D offset = registerAndGetChunkOffset(dimensions, cached);
}
} else {
try {
player.sendMessage(BBC.getPrefix() + "Converting: " + file);
cached.createNewFile();
try (FileInputStream in = new FileInputStream(file)) {
ClipboardFormat format = ClipboardFormat.findByFile(file);
ClipboardReader reader = format.getReader(in);
Clipboard clipboard = reader.read(worldData);
clipboard.setOrigin(clipboard.getMinimumPoint());
try {
MCAQueue queue = new MCAQueue(null, null, false);
BlockVector dimensions = clipboard.getDimensions().toBlockVector();
BlockVector2D offset = registerAndGetChunkOffset(dimensions, cached);
new Schematic(clipboard).paste(queue, Vector.ZERO, true);
try (FileOutputStream fos = new FileOutputStream(cached)) {
IOUtil.writeVarInt(fos, dimensions.getBlockX());
IOUtil.writeVarInt(fos, dimensions.getBlockY());
IOUtil.writeVarInt(fos, dimensions.getBlockZ());
try (FaweOutputStream cos = MainUtil.getCompressedOS(fos, 2)) {
NBTOutputStream nos = new NBTOutputStream((DataOutput) cos);
Collection<FaweChunk> writeChunks = queue.getFaweChunks();
cos.writeInt(writeChunks.size());
boolean selected = isSelected(file);
for (FaweChunk chunk : writeChunks) {
MCAChunk mcaChunk = ((MCAChunk) chunk);
mcaChunk.write(nos);
mcaChunk.setLoc(this, mcaChunk.getX() + offset.getBlockX(), mcaChunk.getZ() + offset.getBlockZ());
if (Math.abs(mcaChunk.getX()) <= 15 && Math.abs(mcaChunk.getZ()) <= 15) {
cacheChunk(cached, mcaChunk, selected);
}
}
}
}
if (System.getProperty("os.name").contains("Windows")) {
Path path = cached.toPath();
Object hidden = java.nio.file.Files.getAttribute(path, "dos:hidden", LinkOption.NOFOLLOW_LINKS);
if (hidden != null) {
//link file to DosFileAttributes
java.nio.file.Files.setAttribute(path, "dos:hidden", Boolean.TRUE, LinkOption.NOFOLLOW_LINKS);
}
}
} finally {
if (clipboard instanceof Closeable) {
((Closeable) clipboard).close();
}
}
}
} catch (Throwable e) {
e.printStackTrace();
cached.delete();
}
}
}
private synchronized MCAChunk getCachedChunk(int chunkX, int chunkZ) {
return chunks.get(MathMan.pairInt(chunkX, chunkZ));
}
private MCAChunk getChunk(int chunkX, int chunkZ) {
long pair = MathMan.pairInt(chunkX, chunkZ);
// Check cached
MCAChunk chunk = getCachedChunk(chunkX, chunkZ);
if (chunk != null) return chunk;
// We need to cache it
Map.Entry<File, Long> entry = files.get(pair);
if (entry != null) {
File cached = entry.getKey();
// Guard caching by other threads
synchronized (cached) {
chunk = getCachedChunk(chunkX, chunkZ);
// Read chunks from disk
if (chunk == null) {
clean();
String filename = cached.getName();
File file = new File(cached.getParentFile(), filename.substring(1, filename.length() - 7));
boolean selected = isSelected(file);
long origin = entry.getValue();
int OCX = MathMan.unpairIntX(origin);
int OCZ = MathMan.unpairIntY(origin);
try {
try (FileInputStream fis = new FileInputStream(cached)) {
BlockVector dimensions = new BlockVector(IOUtil.readVarInt(fis), IOUtil.readVarInt(fis), IOUtil.readVarInt(fis));
try (FaweInputStream in = MainUtil.getCompressedIS(fis)) {
try (NBTInputStream nis = new NBTInputStream(in)) {
int numChunks = in.readInt();
for (int i = 0; i < numChunks; i++) {
MCAChunk mcaChunk = new MCAChunk(nis, null, 0, 0, true);
mcaChunk.setLoc(this, mcaChunk.getX() + OCX, mcaChunk.getZ() + OCZ);
cacheChunk(cached, mcaChunk, selected);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
// Return the cached chunk, or an empty one
chunk = getCachedChunk(chunkX, chunkZ);
if (chunk == null) {
// TODO use shared chunk
// TODO synchronize on sending chunk packet
cacheChunk(cached, chunk = new MCAChunk(this, chunkX, chunkZ), selected);
}
}
}
} else {
chunk = new MCAChunk(this, chunkX, chunkZ);
}
return chunk;
}
/**
* Return a lazily evaluated chunk
* @param chunkX
* @param chunkZ
* @return lazy chunk
*/
@Override
public FaweChunk getSnapshot(int chunkX, int chunkZ) {
return new LazyFaweChunk<MCAChunk>(this, chunkX, chunkZ) {
@Override
public MCAChunk getChunk() {
MCAChunk tmp = SchemVis.this.getChunk(chunkX, chunkZ);
tmp.setLoc(SchemVis.this, getX(), getZ());
return tmp;
}
@Override
public void addToQueue() {
MCAChunk cached = getCachedChunk();
if (cached != null) setChunk(cached);
}
};
}
@Override
public WorldData getWorldData() {
return worldData;
}
@Override
public FawePlayer getPlayer() {
return player;
}
public void bind() {
player.setVirtualWorld(this);
}
/**
* Send all chunks to the player
*/
@Override
public void update() {
FaweQueue packetQueue = SetQueue.IMP.getNewQueue(player.getWorld(), true, false);
if (!packetQueue.supports(Capability.CHUNK_PACKETS)) {
return;
}
int OX = chunkOffset.getBlockX();
int OZ = chunkOffset.getBlockZ();
FaweLocation position = player.getLocation();
int pcx = (position.x >> 4) - OX;
int pcz = (position.z >> 4) - OZ;
int scx = pcx - 15;
int scz = pcz - 15;
int ecx = pcx + 15;
int ecz = pcz + 15;
for (int cz = scz; cz <= ecz; cz++) {
for (int cx = scx; cx <= ecx; cx++) {
final int finalCX = cx;
final int finalCZ = cz;
send(packetQueue, finalCX, finalCZ);
}
}
}
@Override
public void sendChunk(FaweChunk chunk) { /* do nothing - never used */ }
@Override
public void sendChunk(int x, int z, int bitMask) { /* do nothing - never used*/ }
@Override
public int getBiomeId(int x, int z) throws FaweException.FaweChunkLoadException {
// TODO later (currently not used)
return 0;
}
@Override
public int getCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException {
MCAChunk chunk = getChunk(x >> 4, z >> 4);
if (y < 0 || y > 255) return 0;
return chunk.getBlockCombinedId(x & 15, y, z & 15);
}
/**
* Closes this virtual world and sends the normal world chunks to the player
* @throws IOException
*/
@Override
public synchronized void close(boolean update) throws IOException {
clear();
chunks.clear();
files.clear();
player.getPlayer().setPosition(origin.toVector(), origin.getPitch(), origin.getYaw());
if (update) {
FaweQueue packetQueue = SetQueue.IMP.getNewQueue(player.getWorld(), true, false);
int OX = chunkOffset.getBlockX();
int OZ = chunkOffset.getBlockZ();
FaweLocation position = player.getLocation();
int pcx = (position.x >> 4) - OX;
int pcz = (position.z >> 4) - OZ;
int scx = pcx - 15;
int scz = pcz - 15;
int ecx = pcx + 15;
int ecz = pcz + 15;
for (int cz = scz; cz <= ecz; cz++) {
for (int cx = scx; cx <= ecx; cx++) {
packetQueue.sendChunk(cx + OX, cz + OZ, 0);
}
}
}
}
}

View File

@ -0,0 +1,56 @@
package com.boydti.fawe.util;
import java.io.*;
import java.net.URI;
public final class IOUtil {
public InputStream toInputStream(URI uri) throws IOException {
String scheme = uri.getScheme();
switch (scheme.toLowerCase()) {
case "file":
return new FileInputStream(uri.getPath());
case "http":
case "https":
return uri.toURL().openStream();
default:
return null;
}
}
public static final int readInt(InputStream in) throws IOException {
int ch1 = in.read();
int ch2 = in.read();
int ch3 = in.read();
int ch4 = in.read();
if ((ch1 | ch2 | ch3 | ch4) < 0)
throw new EOFException();
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
}
public static final void writeInt(OutputStream out, int v) throws IOException {
out.write((v >>> 24) & 0xFF);
out.write((v >>> 16) & 0xFF);
out.write((v >>> 8) & 0xFF);
out.write((v >>> 0) & 0xFF);
}
public static void writeVarInt(OutputStream out, int i) throws IOException {
while((i & -128) != 0) {
out.write(i & 127 | 128);
i >>>= 7;
}
out.write(i);
}
public static int readVarInt(InputStream in) throws IOException {
int i = 0;
int offset = 0;
int b;
while ((b = in.read()) > 127) {
i |= (b - 128) << offset;
offset += 7;
}
i |= b << offset;
return i;
}
}

View File

@ -19,6 +19,8 @@
package com.sk89q.worldedit;
import java.io.IOException;
/**
* Extension of {@code Vector} that that compares with other instances
* using integer components.
@ -93,6 +95,19 @@ public class BlockVector extends Vector {
return ((int) getX() ^ ((int) getZ() << 16)) ^ ((int) getY() << 30);
}
private void writeObject(java.io.ObjectOutputStream stream) throws IOException {
if (!(this instanceof MutableBlockVector)) {
stream.writeInt(getBlockX());
stream.writeInt(getBlockY());
stream.writeInt(getBlockZ());
}
}
private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException {
if (this instanceof MutableBlockVector) return;
this.setComponents(stream.readInt(), stream.readInt(), stream.readInt());
}
@Override
public BlockVector toBlockVector() {
return this;

View File

@ -25,12 +25,12 @@ import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.example.MappedFaweQueue;
import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator;
import com.boydti.fawe.jnbt.anvil.MCAQueue;
import com.boydti.fawe.jnbt.anvil.MCAWorld;
import com.boydti.fawe.logging.LoggingChangeSet;
import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory;
import com.boydti.fawe.object.*;
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
import com.boydti.fawe.object.changeset.*;
import com.boydti.fawe.object.clipboard.WorldCopyClipboard;
import com.boydti.fawe.object.collection.LocalBlockVectorSet;
@ -297,7 +297,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue,
}
}
if (allowedRegions == null) {
if (player != null && !player.hasPermission("fawe.bypass") && !player.hasPermission("fawe.bypass.regions") && !(queue instanceof HeightMapMCAGenerator)) {
if (player != null && !player.hasPermission("fawe.bypass") && !player.hasPermission("fawe.bypass.regions") && !(queue instanceof VirtualWorld)) {
allowedRegions = player.getCurrentRegions();
}
}

View File

@ -26,9 +26,11 @@ import com.boydti.fawe.object.FaweInputStream;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
import com.boydti.fawe.object.changeset.AnvilHistory;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.object.changeset.FaweChangeSet;
import com.boydti.fawe.object.clipboard.MultiClipboardHolder;
import com.boydti.fawe.object.collection.SparseBitSet;
import com.boydti.fawe.object.extent.ResettableExtent;
import com.boydti.fawe.util.EditSessionBuilder;
@ -41,12 +43,7 @@ import com.sk89q.jchronic.Options;
import com.sk89q.jchronic.utils.Span;
import com.sk89q.jchronic.utils.Time;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.command.tool.BlockTool;
import com.sk89q.worldedit.command.tool.BrushHolder;
import com.sk89q.worldedit.command.tool.BrushTool;
import com.sk89q.worldedit.command.tool.InvalidToolBindException;
import com.sk89q.worldedit.command.tool.SinglePickaxe;
import com.sk89q.worldedit.command.tool.Tool;
import com.sk89q.worldedit.command.tool.*;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.inventory.BlockBag;
@ -65,21 +62,10 @@ import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.snapshot.Snapshot;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;
import java.io.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -138,6 +124,8 @@ public class LocalSession {
private transient UUID uuid;
private transient volatile long historySize = 0;
private transient VirtualWorld virtual;
// Saved properties
private String lastScript;
private RegionSelectorType defaultSelector;
@ -733,6 +721,29 @@ public class LocalSession {
return selector.getRegion();
}
public synchronized @Nullable VirtualWorld getVirtualWorld() {
return virtual;
}
public void setVirtualWorld(@Nullable VirtualWorld world) {
VirtualWorld tmp;
synchronized (this) {
tmp = this.virtual;
this.virtual = world;
}
if (tmp != null) {
try {
tmp.close(world == null);
} catch (IOException e) {
e.printStackTrace();
}
}
if (world != null) {
Fawe.imp().registerPacketListener();
world.update();
}
}
/**
* Get the selection world.
*
@ -785,6 +796,24 @@ public class LocalSession {
return clipboard;
}
public void addClipboard(@Nonnull MultiClipboardHolder toAppend) {
checkNotNull(toAppend);
ClipboardHolder existing = getExistingClipboard();
MultiClipboardHolder multi;
if (existing instanceof MultiClipboardHolder) {
multi = (MultiClipboardHolder) existing;
for (ClipboardHolder holder : toAppend.getHolders()) {
multi.add(holder);
}
} else {
multi = toAppend;
if (existing != null) {
multi.add(existing);
}
}
setClipboard(multi);
}
/**
* Sets the clipboard.
* <p>

View File

@ -78,4 +78,33 @@ public final class MutableBlockVector2D extends BlockVector2D implements Seriali
this.x = stream.readInt();
this.z = stream.readInt();
}
public MutableBlockVector2D nextPosition() {
int absX = Math.abs(x);
int absY = Math.abs(z);
if (absX > absY) {
if (x > 0) {
return setComponents(x, z + 1);
} else {
return setComponents(x, z - 1);
}
} else if (absY > absX) {
if (z > 0) {
return setComponents(x - 1, z);
} else {
return setComponents(x + 1, z);
}
} else {
if (x == z && x > 0) {
return setComponents(x, z + 1);
}
if (x == absX) {
return setComponents(x, z + 1);
}
if (z == absY) {
return setComponents(x, z - 1);
}
return setComponents(x + 1, z);
}
}
}

View File

@ -889,7 +889,7 @@ public class Vector implements Comparable<Vector>, Serializable {
}
private void writeObject(java.io.ObjectOutputStream stream) throws IOException {
if (!(this instanceof Vector)) {
if (!(this instanceof MutableBlockVector)) {
stream.writeDouble(x);
stream.writeDouble(y);
stream.writeDouble(z);

View File

@ -117,7 +117,7 @@ public class GenerationCommands extends MethodCommands {
@Command(
aliases = {"/image", "/img"},
desc = "Generate an image",
usage = "<imgur> [randomize=true] [complexity=100]",
usage = "<imgur> [randomize=true] [complexity=100] [dimensions]",
min = 1,
max = 3
)

View File

@ -29,6 +29,7 @@ import com.boydti.fawe.object.clipboard.MultiClipboardHolder;
import com.boydti.fawe.object.clipboard.URIClipboardHolder;
import com.boydti.fawe.object.clipboard.remap.ClipboardRemapper;
import com.boydti.fawe.object.schematic.StructureFormat;
import com.boydti.fawe.object.schematic.visualizer.SchemVis;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.chat.Message;
import com.sk89q.minecraft.util.commands.Command;
@ -44,9 +45,7 @@ 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.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
import com.sk89q.worldedit.extent.clipboard.io.SchematicReader;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.session.ClipboardHolder;
@ -54,18 +53,15 @@ import com.sk89q.worldedit.util.command.binding.Switch;
import com.sk89q.worldedit.util.command.parametric.Optional;
import com.sk89q.worldedit.util.io.file.FilenameException;
import com.sk89q.worldedit.world.registry.WorldData;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
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.nio.file.Files;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.atomic.LongAdder;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
@ -110,20 +106,7 @@ public class SchematicCommands extends MethodCommands {
MultiClipboardHolder all = format.loadAllFromInput(player, wd, filename, true);
if (all != null) {
ClipboardHolder existing = session.getExistingClipboard();
MultiClipboardHolder multi;
if (existing instanceof MultiClipboardHolder) {
multi = (MultiClipboardHolder) existing;
for (ClipboardHolder holder : all.getHolders()) {
multi.add(holder);
}
} else {
multi = all;
if (existing != null) {
multi.add(existing);
}
}
session.setClipboard(multi);
session.addClipboard(all);
BBC.SCHEMATIC_LOADED.send(player, filename);
}
} catch (IOException e) {
@ -274,18 +257,7 @@ public class SchematicCommands extends MethodCommands {
uri = f.toURI();
}
final ClipboardReader reader = format.getReader(in);
final WorldData worldData = player.getWorld().getWorldData();
final Clipboard clipboard;
session.setClipboard(null);
if (reader instanceof SchematicReader) {
clipboard = ((SchematicReader) reader).read(player.getWorld().getWorldData(), player.getUniqueId());
} else if (reader instanceof StructureFormat) {
clipboard = ((StructureFormat) reader).read(player.getWorld().getWorldData(), player.getUniqueId());
} else {
clipboard = reader.read(player.getWorld().getWorldData());
}
session.setClipboard(new URIClipboardHolder(uri, clipboard, worldData));
format.hold(player, uri, in);
BBC.SCHEMATIC_LOADED.send(player, filename);
} catch (IllegalArgumentException e) {
player.printError("Unknown filename: " + filename);
@ -380,24 +352,107 @@ public class SchematicCommands extends MethodCommands {
}
}
@Command(aliases = {"delete", "d"}, usage = "<filename>", desc = "Delete a saved schematic", help = "Delete a schematic from the schematic list", min = 1, max = 1)
@CommandPermissions("worldedit.schematic.delete")
public void delete(final Player player, final LocalSession session, final CommandContext args) throws WorldEditException {
@Command(aliases = {"move", "m"}, usage = "<directory>", desc = "Move your loaded schematic", help = "Move your currently loaded schematics", min = 1, max = 1)
@CommandPermissions({"worldedit.schematic.move", "worldedit.schematic.move.other"})
public void move(final Player player, final LocalSession session, final CommandContext args) throws WorldEditException {
final LocalConfiguration config = this.worldEdit.getConfiguration();
final String filename = args.getString(0);
final File working = this.worldEdit.getWorkingDirectoryFile(config.saveDir);
final File dir = Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS ? new File(working, player.getUniqueId().toString()) : working;
final File f = this.worldEdit.getSafeSaveFile(player, dir, filename, "schematic", "schematic");
if (!f.exists()) {
player.printError("Schematic " + filename + " does not exist!");
File destDir = new File(dir, args.getString(0));
if (!MainUtil.isInSubDirectory(working, destDir)) {
player.printError("Directory " + destDir + " does not exist!");
return;
}
if (!f.delete()) {
player.printError("Deletion of " + filename + " failed! Maybe it is read-only.");
if (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS && !MainUtil.isInSubDirectory(dir, destDir) && !player.hasPermission("worldedit.schematic.move.other")) {
BBC.NO_PERM.send(player, "worldedit.schematic.move.other");
return;
}
BBC.FILE_DELETED.send(player, filename);
ClipboardHolder clipboard = session.getClipboard();
List<File> sources = getFiles(clipboard);
if (sources.isEmpty()) {
BBC.SCHEMATIC_NONE.send(player);
return;
}
if (!destDir.exists() && !destDir.mkdirs()) {
player.printError("Creation of " + destDir + " failed! (check file permissions)");
return;
}
for (File source : sources) {
File destFile = new File(destDir, source.getName());
if (destFile.exists()) {
BBC.SCHEMATIC_MOVE_EXISTS.send(player, destFile);
continue;
}
if (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS && (!MainUtil.isInSubDirectory(dir, destFile) || !MainUtil.isInSubDirectory(dir, source)) && !player.hasPermission("worldedit.schematic.delete.other")) {
BBC.SCHEMATIC_MOVE_FAILED.send(player, destFile, BBC.NO_PERM.f("worldedit.schematic.move.other"));
continue;
}
try {
File cached = new File(source.getParentFile(), "." + source.getName() + ".cached");
Files.move(source.toPath(), destFile.toPath());
if (cached.exists()) Files.move(cached.toPath(), destFile.toPath());
BBC.SCHEMATIC_MOVE_SUCCESS.send(player, source, destFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Command(aliases = {"delete", "d"}, usage = "<filename|*>", desc = "Delete a saved schematic", help = "Delete a schematic from the schematic list", min = 1, max = 1)
@CommandPermissions({"worldedit.schematic.delete", "worldedit.schematic.other"})
public void delete(final Player player, final LocalSession session, final CommandContext args) throws WorldEditException {
final LocalConfiguration config = this.worldEdit.getConfiguration();
final File working = this.worldEdit.getWorkingDirectoryFile(config.saveDir);
final File dir = Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS ? new File(working, player.getUniqueId().toString()) : working;
List<File> files = new ArrayList<>();
final String filename = args.getString(0);
if (filename.equalsIgnoreCase("*")) {
files.addAll(getFiles(session.getClipboard()));
} else {
files.add(new File(dir, filename));
}
if (files.isEmpty()) {
BBC.SCHEMATIC_NONE.send(player);
return;
}
for (File f : files) {
if (!MainUtil.isInSubDirectory(working, f) || !f.exists()) {
player.printError("Schematic " + filename + " does not exist! (" + f.exists() + "|" + f + "|" + (!MainUtil.isInSubDirectory(working, f)) + ")");
continue;
}
if (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS && !MainUtil.isInSubDirectory(dir, f) && !player.hasPermission("worldedit.schematic.delete.other")) {
BBC.NO_PERM.send(player, "worldedit.schematic.delete.other");
continue;
}
if (!delete(f)) {
player.printError("Deletion of " + filename + " failed! Maybe it is read-only.");
continue;
}
BBC.FILE_DELETED.send(player, filename);
}
}
private List<File> getFiles(ClipboardHolder clipboard) {
List<File> files = new ArrayList<>();
Collection<URI> uris = Collections.emptyList();
if (clipboard instanceof URIClipboardHolder) {
uris = ((URIClipboardHolder) clipboard).getURIs();
}
for (URI uri : uris) {
File file = new File(uri);
if (file.exists()) files.add(file);
}
return files;
}
private boolean delete(File file) {
if (file.delete()) {
new File(file.getParentFile(), "." + file.getName() + ".cached").delete();
return true;
}
return false;
}
@Command(aliases = {"formats", "listformats", "f"}, desc = "List available formats", max = 0)
@ -424,6 +479,58 @@ public class SchematicCommands extends MethodCommands {
m.send(actor);
}
@Command(
aliases = {"show"},
desc = "Show a schematic",
usage = "[global|mine|<filter>] [page=1]",
min = 0,
max = -1,
flags = "dnp",
help = "List all schematics in the schematics directory\n" +
" -p <page> prints the requested page\n" +
" -f <format> restricts by format\n"
)
@CommandPermissions("worldedit.heightmap.list")
public void show(Player player, CommandContext args, @Switch('p') @Optional("1") int page) {
FawePlayer fp = FawePlayer.wrap(player);
if (args.argsLength() == 0 && fp.getSession().getVirtualWorld() != null) {
fp.setVirtualWorld(null);
return;
}
LocalConfiguration config = worldEdit.getConfiguration();
File dir = worldEdit.getWorkingDirectoryFile(config.saveDir);
try {
SchemVis visExtent = new SchemVis(fp);
LongAdder count = new LongAdder();
UtilityCommands.getFiles(dir, player, args, 0, Character.MAX_VALUE, null, Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS, file -> {
if (file.isFile()) {
try {
visExtent.add(file);
count.add(1);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
visExtent.bind();
visExtent.update();
String cmdPrefix = "/" + (config.noDoubleSlash ? "" : "/");
String cmdShow = Commands.getAlias(ClipboardCommands.class, "schematic") + " " + Commands.getAlias(ClipboardCommands.class, "show");
BBC.SCHEMATIC_SHOW.send(fp, count.longValue(), args.getJoinedStrings(0), cmdShow);
if (fp.getSession().getExistingClipboard() != null) {
String cmd = cmdPrefix + Commands.getAlias(ClipboardCommands.class, "clipboard") + " " + Commands.getAlias(ClipboardCommands.class, "clear");
BBC.SCHEMATIC_PROMPT_CLEAR.send(fp, cmd);
}
} catch (Throwable e) {
fp.setVirtualWorld(null);
throw e;
}
}
@Command(
aliases = {"list", "ls", "all"},
desc = "List saved schematics",
@ -454,7 +561,7 @@ public class SchematicCommands extends MethodCommands {
URIClipboardHolder multi = as(URIClipboardHolder.class, fp.getSession().getExistingClipboard());
UtilityCommands.list(dir, actor, args, page, formatName, Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS, new RunnableVal3<Message, URI, String>() {
UtilityCommands.list(dir, actor, args, page, -1, formatName, Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS, new RunnableVal3<Message, URI, String>() {
@Override
public void run(Message msg, URI uri, String relFilePath) {
boolean isDir = false;

View File

@ -24,6 +24,7 @@ import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.command.FaweParser;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Commands;
import com.boydti.fawe.object.DelegateConsumer;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RunnableVal3;
@ -74,8 +75,8 @@ import java.lang.reflect.Type;
import java.net.URI;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import static com.sk89q.minecraft.util.commands.Logging.LogMode.PLACEMENT;
@ -619,7 +620,7 @@ public class UtilityCommands extends MethodCommands {
}
public static void list(File dir, Actor actor, CommandContext args, @Range(min = 0) int page, String formatName, boolean playerFolder, String onClickCmd) {
list(dir, actor, args, page, formatName, playerFolder, new RunnableVal3<Message, URI, String>() {
list(dir, actor, args, page, -1, formatName, playerFolder, new RunnableVal3<Message, URI, String>() {
@Override
public void run(Message m, URI uri, String fileName) {
m.text(BBC.SCHEMATIC_LIST_ELEM, fileName, "");
@ -628,7 +629,75 @@ public class UtilityCommands extends MethodCommands {
});
}
public static void list(File dir, Actor actor, CommandContext args, @Range(min = 0) int page, String formatName, boolean playerFolder, RunnableVal3<Message, URI, String> eachMsg) {
public static void list(File dir, Actor actor, CommandContext args, @Range(min = 0) int page, int perPage, String formatName, boolean playerFolder, RunnableVal3<Message, URI, String> eachMsg) {
AtomicInteger pageInt = new AtomicInteger(page);
List<File> fileList = new ArrayList<>();
page = getFiles(dir, actor, args, page, perPage, formatName, playerFolder, file -> fileList.add(file));
if (fileList.isEmpty()) {
BBC.SCHEMATIC_NONE.send(actor);
return;
}
if (perPage == -1) perPage = actor instanceof Player ? 12 : 20; // More pages for console
int pageCount = (fileList.size() + perPage - 1) / perPage;
if (page < 1) {
BBC.SCHEMATIC_PAGE.send(actor, ">0");
return;
}
if (page > pageCount) {
BBC.SCHEMATIC_PAGE.send(actor, "<" + (pageCount + 1));
return;
}
final int sortType = args.hasFlag('d') ? -1 : args.hasFlag('n') ? 1 : 0;
// cleanup file list
Collections.sort(fileList, new Comparator<File>() {
@Override
public int compare(File f1, File f2) {
boolean dir1 = f1.isDirectory();
boolean dir2 = f2.isDirectory();
if (dir1 != dir2) return dir1 ? -1 : 1;
int res;
if (sortType == 0) { // use name by default
int p = f1.getParent().compareTo(f2.getParent());
if (p == 0) { // same parent, compare names
res = f1.getName().compareTo(f2.getName());
} else { // different parent, sort by that
res = p;
}
} else {
res = Long.valueOf(f1.lastModified()).compareTo(f2.lastModified()); // use date if there is a flag
if (sortType == 1) res = -res; // flip date for newest first instead of oldest first
}
return res;
}
});
int offset = (page - 1) * perPage;
int limit = Math.min(offset + perPage, fileList.size());
String fullArgs = (String) args.getLocals().get("arguments");
String baseCmd = null;
if (fullArgs != null) {
baseCmd = fullArgs.endsWith(" " + page) ? fullArgs.substring(0, fullArgs.length() - (" " + page).length()) : fullArgs;
}
Message m = new Message(BBC.SCHEMATIC_LIST, page, pageCount);
UUID uuid = playerFolder ? actor.getUniqueId() : null;
for (int i = offset; i < limit; i++) {
m.newline();
File file = fileList.get(i);
eachMsg.run(m, file.toURI(), getPath(dir, file, uuid));
}
if (baseCmd != null) {
m.newline().paginate(baseCmd, page, pageCount);
}
m.send(actor);
}
protected static int getFiles(File dir, Actor actor, CommandContext args, @Range(min = 0) int page, int perPage, String formatName, boolean playerFolder, Consumer<File> forEachFile) {
int len = args.argsLength();
List<String> filters = new ArrayList<>();
@ -696,90 +765,49 @@ public class UtilityCommands extends MethodCommands {
return true;
};
List<File> fileList = new ArrayList<>();
if (formatName != null) {
final ClipboardFormat cf = ClipboardFormat.findByAlias(formatName);
forEachFile = new DelegateConsumer<File>(forEachFile) {
@Override
public void accept(File file) {
if (cf.isFormat(file)) super.accept(file);
}
};
} else {
forEachFile = new DelegateConsumer<File>(forEachFile) {
@Override
public void accept(File file) {
if (!file.toString().endsWith(".cached")) super.accept(file);
}
};
}
if (playerFolder) {
if (listMine) {
File playerDir = new File(dir, actor.getUniqueId() + dirFilter);
if (playerDir.exists()) fileList.addAll(allFiles(playerDir.listFiles(), false));
if (playerDir.exists()) allFiles(playerDir.listFiles(), false, forEachFile);
}
if (listGlobal) {
File rel = new File(dir, dirFilter);
if (rel.exists()) fileList.addAll(allFiles(rel.listFiles(ignoreUUIDs), false));
forEachFile = new DelegateConsumer<File>(forEachFile) {
@Override
public void accept(File f) {
try {
if (f.isDirectory()) {
UUID uuid = UUID.fromString(f.getName());
return;
}
} catch (IllegalArgumentException exception) {}
super.accept(f);
}
};
if (rel.exists()) allFiles(rel.listFiles(), false, forEachFile);
}
} else {
File rel = new File(dir, dirFilter);
if (rel.exists()) fileList.addAll(allFiles(rel.listFiles(), false));
if (rel.exists()) allFiles(rel.listFiles(), false, forEachFile);
}
if (fileList.isEmpty()) {
BBC.SCHEMATIC_NONE.send(actor);
return;
}
if (formatName != null) {
final ClipboardFormat cf = ClipboardFormat.findByAlias(formatName);
fileList = fileList.stream()
.filter(file -> cf.isFormat(file))
.collect(Collectors.toList());
}
fileList = filter(fileList, filters);
final int perPage = actor instanceof Player ? 12 : 20; // More pages for console
int pageCount = (fileList.size() + perPage - 1) / perPage;
if (page < 1) {
BBC.SCHEMATIC_PAGE.send(actor, ">0");
return;
}
if (page > pageCount) {
BBC.SCHEMATIC_PAGE.send(actor, "<" + (pageCount + 1));
return;
}
final int sortType = args.hasFlag('d') ? -1 : args.hasFlag('n') ? 1 : 0;
// cleanup file list
Collections.sort(fileList, new Comparator<File>() {
@Override
public int compare(File f1, File f2) {
boolean dir1 = f1.isDirectory();
boolean dir2 = f2.isDirectory();
if (dir1 != dir2) return dir1 ? -1 : 1;
int res;
if (sortType == 0) { // use name by default
int p = f1.getParent().compareTo(f2.getParent());
if (p == 0) { // same parent, compare names
res = f1.getName().compareTo(f2.getName());
} else { // different parent, sort by that
res = p;
}
} else {
res = Long.valueOf(f1.lastModified()).compareTo(f2.lastModified()); // use date if there is a flag
if (sortType == 1) res = -res; // flip date for newest first instead of oldest first
}
return res;
}
});
int offset = (page - 1) * perPage;
int limit = Math.min(offset + perPage, fileList.size());
String fullArgs = (String) args.getLocals().get("arguments");
String baseCmd = null;
if (fullArgs != null) {
baseCmd = fullArgs.endsWith(" " + page) ? fullArgs.substring(0, fullArgs.length() - (" " + page).length()) : fullArgs;
}
Message m = new Message(BBC.SCHEMATIC_LIST, page, pageCount);
UUID uuid = playerFolder ? actor.getUniqueId() : null;
for (int i = offset; i < limit; i++) {
m.newline();
File file = fileList.get(i);
eachMsg.run(m, file.toURI(), getPath(dir, file, uuid));
}
if (baseCmd != null) {
m.newline().paginate(baseCmd, page, pageCount);
}
m.send(actor);
return page;
}
private static List<File> filter(List<File> fileList, List<String> filters) {
@ -822,23 +850,19 @@ public class UtilityCommands extends MethodCommands {
return fileList;
}
private static List<File> allFiles(File[] files, boolean recursive) {
if (files == null || files.length == 0) return Arrays.asList();
List<File> fileList = new ArrayList<File>();
private static void allFiles(File[] files, boolean recursive, Consumer<File> task) {
if (files == null || files.length == 0) return;
for (File f : files) {
if (f.isDirectory()) {
if (recursive) {
List<File> subFiles = allFiles(f.listFiles(), recursive);
if (subFiles == null || subFiles.isEmpty()) continue; // empty subdir
fileList.addAll(subFiles);
allFiles(f.listFiles(), recursive, task);
} else {
fileList.add(f);
task.accept(f);
}
} else {
fileList.add(f);
task.accept(f);
}
}
return fileList;
}
private static String getPath(File root, File file, UUID uuid) {

View File

@ -21,6 +21,7 @@ package com.sk89q.worldedit.extension.platform;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.object.pattern.PatternTraverser;
import com.boydti.fawe.util.MainUtil;
@ -328,10 +329,6 @@ public class PlatformManager {
return tool;
}
public void handleMove() {
}
@SuppressWarnings("deprecation")
@Subscribe
public void handleBlockInteract(BlockInteractEvent event) {
@ -347,6 +344,13 @@ public class PlatformManager {
if (actor instanceof Player) {
final LocalSession session = worldEdit.getSessionManager().get(actor);
Player playerActor = (Player) actor;
VirtualWorld virtual = session.getVirtualWorld();
if (virtual != null) {
virtual.handleBlockInteract(playerActor, vector, event);
if (event.isCancelled()) return;
}
if (event.getType() == Interaction.HIT) {
if (session.isToolControlEnabled() && playerActor.getItemInHand() == getConfiguration().wandItem) {
FawePlayer<?> fp = FawePlayer.wrap(playerActor);
@ -394,6 +398,7 @@ public class PlatformManager {
}
}, true, true);
event.setCancelled(true);
return;
}
}
} else if (event.getType() == Interaction.OPEN) {
@ -402,17 +407,18 @@ public class PlatformManager {
if (!actor.hasPermission("worldedit.selection.pos")) {
return;
}
final RegionSelector selector = session.getRegionSelector(playerActor.getWorld());
final Player player = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation());
fp.runAction(new Runnable() {
@Override
public void run() {
if (selector.selectSecondary(vector, ActorSelectorLimits.forActor(player))) {
selector.explainSecondarySelection(actor, session, vector);
if (fp.checkAction()) {
final RegionSelector selector = session.getRegionSelector(playerActor.getWorld());
final Player player = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation());
fp.runAction(new Runnable() {
@Override
public void run() {
if (selector.selectSecondary(vector, ActorSelectorLimits.forActor(player))) {
selector.explainSecondarySelection(actor, session, vector);
}
}
}
}, true, true);
}, true, true);
}
event.setCancelled(true);
return;
}
@ -421,18 +427,21 @@ public class PlatformManager {
if (tool != null && tool instanceof BlockTool) {
if (tool.canUse(playerActor)) {
FawePlayer<?> fp = FawePlayer.wrap(playerActor);
final Player player = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation());
fp.runAction(new Runnable() {
@Override
public void run() {
if (tool instanceof BrushTool) {
((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
} else {
reset((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
if (fp.checkAction()) {
final Player player = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation());
fp.runAction(new Runnable() {
@Override
public void run() {
if (tool instanceof BrushTool) {
((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
} else {
reset((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
}
}
}
}, true, true);
event.setCancelled(true);
}, true, true);
event.setCancelled(true);
return;
}
}
}
}
@ -460,6 +469,13 @@ public class PlatformManager {
// making changes to the world
Player actor = createProxyActor(event.getPlayer());
final Player player = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap(actor), actor.getLocation(), true);
final LocalSession session = worldEdit.getSessionManager().get(player);
VirtualWorld virtual = session.getVirtualWorld();
if (virtual != null) {
virtual.handlePlayerInput(player, event);
if (event.isCancelled()) return;
}
try {
switch (event.getInputType()) {
@ -484,8 +500,6 @@ public class PlatformManager {
return;
}
final LocalSession session = worldEdit.getSessionManager().get(player);
final Tool tool = session.getTool(player);
if (tool != null && tool instanceof DoubleActionTraceTool) {
if (tool.canUse(player)) {
@ -521,8 +535,6 @@ public class PlatformManager {
return;
}
final LocalSession session = worldEdit.getSessionManager().get(player);
final Tool tool = session.getTool(player);
if (tool != null && tool instanceof TraceTool) {
if (tool.canUse(player)) {

View File

@ -38,7 +38,7 @@ public interface Extent extends InputExtent, OutputExtent {
default
@Nullable
Entity createEntity(Location location, BaseEntity entity) {
throw new UnsupportedOperationException(getClass() + " does not support entity creation!");
return null;
}
@Override
@ -240,7 +240,7 @@ public interface Extent extends InputExtent, OutputExtent {
}
}
default boolean contain(Vector pt) {
default boolean contains(Vector pt) {
Vector min = getMinimumPoint();
Vector max = getMaximumPoint();
return (pt.containedWithin(min, max));

View File

@ -39,6 +39,7 @@ import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.biome.BaseBiome;
import com.sk89q.worldedit.world.registry.BundledBlockData;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -52,7 +53,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
* Stores block data as a multi-dimensional array of {@link BaseBlock}s and
* other data as lists or maps.
*/
public class BlockArrayClipboard implements Clipboard, LightingExtent {
public class BlockArrayClipboard implements Clipboard, LightingExtent, Closeable {
private Region region;
public FaweClipboard IMP;
@ -120,6 +121,7 @@ public class BlockArrayClipboard implements Clipboard, LightingExtent {
close();
}
@Override
public void close() {
IMP.close();
}

View File

@ -24,13 +24,7 @@ import com.boydti.fawe.config.Settings;
import com.boydti.fawe.jnbt.NBTStreamer;
import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.clipboard.AbstractClipboardFormat;
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
import com.boydti.fawe.object.clipboard.FaweClipboard;
import com.boydti.fawe.object.clipboard.IClipboardFormat;
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.clipboard.*;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.boydti.fawe.object.io.PGZIPOutputStream;
import com.boydti.fawe.object.io.ResettableFileInputStream;
@ -47,32 +41,22 @@ import com.sk89q.jnbt.NBTConstants;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream;
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.registry.WorldData;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
@ -481,6 +465,38 @@ public enum ClipboardFormat {
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 WorldData worldData = player.getWorld().getWorldData();
final Clipboard clipboard;
LocalSession session = WorldEdit.getInstance().getSessionManager().get(player);
session.setClipboard(null);
if (reader instanceof SchematicReader) {
clipboard = ((SchematicReader) reader).read(player.getWorld().getWorldData(), player.getUniqueId());
} else if (reader instanceof StructureFormat) {
clipboard = ((StructureFormat) reader).read(player.getWorld().getWorldData(), player.getUniqueId());
} else {
clipboard = reader.read(player.getWorld().getWorldData());
}
URIClipboardHolder holder = new URIClipboardHolder(uri, clipboard, worldData);
session.setClipboard(holder);
return holder;
}
public Schematic load(File file) throws IOException {
return load(new FileInputStream(file));
}

View File

@ -68,7 +68,7 @@ public class FaweForge implements IFawe {
@Override
public String getPlatformVersion() {
return FMLCommonHandler.instance().getMinecraftServerInstance().getMinecraftVersion();
return "1.12";
}
@Override