package schempaster;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Sign;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitTask;
public class SchemPaster extends JavaPlugin
{
private World world;
private EditSession session;
private String folder = "";
private YamlConfiguration yaml = new YamlConfiguration();
private List<Long> pasttimes = new ArrayList();
private double tps;
private final Object tpslock = new Object();
@Override
public void onEnable()
{
pasttimes = new ArrayList(Arrays.asList(0l, 0l));
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, () ->
{
synchronized (tpslock)
{
pasttimes.add(System.currentTimeMillis());
if (pasttimes.size() > 20)
pasttimes.remove((int) 0);
tps = IntStream.range(0, pasttimes.size() - 1)
.map(i -> (int) (pasttimes.get(i + 1) - pasttimes.get(i)))
.sum() / (double) (pasttimes.size() - 1) / 50d * 20d;
}
}, 0, 1);
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
{
if (sender != Bukkit.getConsoleSender())
{
sender.sendMessage(ChatColor.RED + "Console only!");
return true;
}
if (Bukkit.isPrimaryThread())
{
if (args.length != 1 && args.length != 2)
{
getLogger().severe("/schempaster <world> [schematic folder]");
return true;
}
world = Bukkit.getWorld(args[0]);
if (world == null)
{
getLogger().severe("World not found");
return true;
}
session = WorldEdit.getInstance().getEditSessionFactory().getEditSession(new BukkitWorld(world), -1);
folder = "";
if (args.length == 2)
{
folder = args[1] + "/";
}
new Thread(() -> onCommand(sender, command, label, args)).start();
return true;
}
System.out.println("searching " + new File(folder).getAbsolutePath());
List<File> files = getAllFiles(new File(folder));
files = files.stream().filter(e -> !("" + e).endsWith(".yml")).collect(Collectors.toList());
getLogger().info("Found " + files.size() + " files. Pasting now ...");
List<Map.Entry<Long, Integer>> timings = new ArrayList();
int currentx = 0;
for (File f : files)
{
getLogger().info("---------------------------------------------------------------------------");
long now = System.currentTimeMillis();
if (!timings.isEmpty())
{
float schempm = (float) (timings.stream().filter(e -> e.getKey() > now - 5 * 60 * 1000).count() / 5d);
schempm = (float) (Math.floor(100 * schempm) / 100);
int eta = (int) ((files.size() - files.indexOf(f)) / (schempm / 60));
float progress = 100f * files.indexOf(f) / files.size();
progress = (float) (Math.floor(100 * progress) / 100);
getLogger().info("Status"
+ " " + (files.indexOf(f) + 1) + "/" + files.size() + "(" + progress + "%);"
+ " eta: " + (eta / 60) + ":" + (eta % 60) + ";"
+ " " + schempm + " Schem/m");
}
getLogger().info("Pasting " + f);
//load
Clipboard c;
ClipboardFormat format = ClipboardFormats.findByFile(f);
try(FileInputStream fis = new FileInputStream(f);
BufferedInputStream bis = new BufferedInputStream(fis);
ClipboardReader reader = format.getReader(bis);)
{
c = reader.read();
}
catch (Exception e)
{
getLogger().severe("Failed to load schematic: " + f.getAbsolutePath());
e.printStackTrace();
continue;
}
//compute paste point
BlockVector3 min = BlockVector3.at(currentx, 1, 0);
BlockVector3 max = min.add(c.getRegion().getMaximumPoint().subtract(c.getRegion().getMinimumPoint()));
BlockVector3 rel2schematicpastepoint = c.getOrigin().subtract(c.getRegion().getMinimumPoint());
BlockVector3 pastepoint = min.add(rel2schematicpastepoint);
ProtectedCuboidRegion pr = new ProtectedCuboidRegion(f.toString().replaceAll("\\..*?$", "").replaceAll("[^a-zA-Z0-9]", "_"),
min, max);
List<BlockVector2> chunks = chunks(min.subtract(1, 1, 1), max.add(1, 1, 1));
List<Location> box = box(min.subtract(1, 1, 1), max.add(1, 1, 1));
List<Location> outline = outline(min.subtract(1, 1, 1), max.add(1, 1, 1));
// System.out.println("min " + min);
// System.out.println("max " + max);
// System.out.println("pp " + pastepoint);
getLogger().info("Pasting " + c.getDimensions() + " (" + c.getRegion().getArea() + " blocks) ...");
//paste
Operation op = new ClipboardHolder(c)
.createPaste(session)
.to(pastepoint)
.copyBiomes(false)
.copyEntities(true)
.build();
BukkitTask task = Bukkit.getScheduler().runTask(this, () ->
{
chunks.forEach(e -> world.getChunkAt(e.getBlockX(), e.getBlockZ()).load(true));
session.setFastMode(true);
Operations.completeBlindly(op);
session.flushSession();
WorldEdit.getInstance().flushBlockBag(null, session);
box.forEach(e -> e.getBlock().setType(Material.BARRIER));
outline.forEach(e -> e.getBlock().setType(Material.BEDROCK));
world.getBlockAt(min.getBlockX(), min.getBlockY() + 4, min.getBlockZ() - 2).setType(Material.LAPIS_BLOCK);
world.getBlockAt(min.getBlockX(), min.getBlockY() + 5, min.getBlockZ() - 2).setType(Material.REDSTONE_BLOCK);
world.getBlockAt(min.getBlockX() + 1, min.getBlockY() + 4, min.getBlockZ() - 2).setType(Material.OBSIDIAN);
world.getBlockAt(min.getBlockX() + 1, min.getBlockY() + 4, min.getBlockZ() - 3).setType(Material.OAK_WALL_SIGN);
Sign s = (Sign) world.getBlockAt(min.getBlockX() + 1, min.getBlockY() + 4, min.getBlockZ() - 3).getState();
String sx = "" + f;
sx = sx.substring(Math.max(0, sx.length() - 15 * 4), sx.length());
s.setLine(0, sx.substring(0, Math.min(15, sx.length())));
s.setLine(1, sx.substring(Math.min(15, sx.length()), Math.min(30, sx.length())));
s.setLine(2, sx.substring(Math.min(30, sx.length()), Math.min(45, sx.length())));
s.setLine(3, sx.substring(Math.min(45, sx.length()), Math.min(60, sx.length())));
s.update();
RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(new BukkitWorld(world));
rm.addRegion(pr);
try
{
rm.save();
}
catch (Exception e)
{
getLogger().severe("WG failed to save regions!");
e.printStackTrace();
}
});
//wait for copy
while (Bukkit.getScheduler().isQueued(task.getTaskId()) || Bukkit.getScheduler().isCurrentlyRunning(task.getTaskId()))
{
try
{
Thread.sleep(5000);
}
catch (InterruptedException ex)
{
}
}
yaml.set(pr.getId() + ".file", "" + f);
yaml.set(pr.getId() + ".origin.x", rel2schematicpastepoint.getX());
yaml.set(pr.getId() + ".origin.y", rel2schematicpastepoint.getY());
yaml.set(pr.getId() + ".origin.z", rel2schematicpastepoint.getZ());
currentx += c.getRegion().getWidth() + 10;
try
{
getLogger().info("Waiting for server to recover.");
// Thread.sleep(2000);
long lastprint = 0;
while (tps < 10 || tps > 20.5 || System.currentTimeMillis() - lastTick() > 100)
{
long ago = System.currentTimeMillis() - lastTick();
if (ago > 2000 && ago % 5000 < 100 && (ago - lastprint) > 5000)
{
getLogger().info("tps: " + tps + "; last tick " + (System.currentTimeMillis() - lastTick()) + "ms ago");
lastprint = ago;
}
Thread.sleep(500);
}
// getLogger().info("Continuing");
}
catch (InterruptedException ex)
{
}
int dur = (int) (System.currentTimeMillis() - now);
timings.add(new SimpleEntry(now, dur));
}
try
{
getLogger().info("Save paste infos ...");
yaml.save(new File(getDataFolder().getAbsolutePath() + "/" + folder, "pastes.yml"));
}
catch (IOException ex)
{
sender.sendMessage("Failed to save import data.");
ex.printStackTrace();
}
getLogger().info("Finished!");
return true;
}
private List<File> getAllFiles(File f)
{
List<File> ret = new ArrayList();
String[] list = f.list();
Arrays.sort(list, (l, r) -> l.toLowerCase().compareTo(r.toLowerCase()));
Arrays.stream(list).map(e -> new File(f, e)).forEach(e ->
{
if (e.isDirectory())
ret.addAll(getAllFiles(e));
else if (e.isFile())
{
ret.add(e);
}
});
return ret;
}
private List<BlockVector2> chunks(BlockVector3 min, BlockVector3 max)
{
min = min.subtract(48, 0, 48);
max = max.add(48, 0, 48);
BlockVector2 min2 = BlockVector2.at(min.getBlockX() >> 4, min.getBlockZ() >> 4);
BlockVector2 max2 = BlockVector2.at(max.getBlockX() >> 4, max.getBlockZ() >> 4);
return IntStream.range(min2.getBlockX(), max2.getBlockX() + 1)
.mapToObj(x
-> IntStream.range(min2.getBlockZ(), max2.getBlockZ() + 1)
.mapToObj(z -> BlockVector2.at(x, z)))
.flatMap(e -> e)
.collect(Collectors.toList());
}
private List<Location> box(BlockVector3 min, BlockVector3 max)
{
return IntStream.range(min.getBlockX(), max.getBlockX() + 1)
.mapToObj(x
-> IntStream.range(min.getBlockY(), max.getBlockY() + 1)
.mapToObj(y
-> IntStream.range(min.getBlockZ(), max.getBlockZ() + 1)
.mapToObj(z -> new Location(world, x, y, z)))
.flatMap(e -> e))
.flatMap(e -> e)
.filter(e -> ((e.getBlockX() == min.getBlockX() || e.getBlockX() == max.getBlockX()) ? 1 : 0)
+ ((e.getBlockY() == min.getBlockY() || e.getBlockY() == max.getBlockY()) ? 1 : 0)
+ ((e.getBlockZ() == min.getBlockZ() || e.getBlockZ() == max.getBlockZ()) ? 1 : 0) >= 1)
.collect(Collectors.toList());
}
private List<Location> outline(BlockVector3 min, BlockVector3 max)
{
return IntStream.range(min.getBlockX(), max.getBlockX() + 1)
.mapToObj(x
-> IntStream.range(min.getBlockY(), max.getBlockY() + 1)
.mapToObj(y
-> IntStream.range(min.getBlockZ(), max.getBlockZ() + 1)
.mapToObj(z -> new Location(world, x, y, z)))
.flatMap(e -> e))
.flatMap(e -> e)
.filter(e -> ((e.getBlockX() == min.getBlockX() || e.getBlockX() == max.getBlockX()) ? 1 : 0)
+ ((e.getBlockY() == min.getBlockY() || e.getBlockY() == max.getBlockY()) ? 1 : 0)
+ ((e.getBlockZ() == min.getBlockZ() || e.getBlockZ() == max.getBlockZ()) ? 1 : 0) >= 2)
.collect(Collectors.toList());
}
private long lastTick()
{
synchronized (tpslock)
{
return pasttimes.get(pasttimes.size() - 1);
}
}
}