package de.fanta.challenges.utils;
import de.cubeside.nmsutils.NMSUtils;
import de.fanta.challenges.Challenges;
import de.fanta.challenges.SaveSlot;
import de.fanta.challenges.ServerType;
import de.fanta.challenges.events.ChallengeEventStatusChangedEvent;
import de.fanta.challenges.events.EventStatusChangedEvent;
import de.fanta.challenges.events.ServerStatusChangedEvent;
import net.kyori.adventure.text.Component;
import org.apache.commons.io.FileUtils;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
public class SaveWorldUtils {
private static final Challenges plugin = Challenges.getPlugin();
public static boolean isSavingWorld = false;
public static boolean isCopyWorld = false;
private static boolean isServerRestartRequested = false;
private static boolean restart = false;
private static String restartSeed = null;
public static double progress = 0.0;
public static void saveWorld(String saveID, SaveSlot saveSlot) {
saveWorld(null, saveID, saveSlot);
}
public static void saveWorld(@Nullable Player player, String saveID, SaveSlot saveSlot) {
plugin.getLogger().info("Try Save world slot: " + saveSlot.getSlot());
try {
if (isSavingWorld) {
if (player != null) {
ChatUtil.sendErrorMessage(player, "Diese Welt wird aktuell gespeichert. Bitte warte bis der Vorgang abgeschlossen ist. Dies kann einige Minuten dauern.");
}
return;
}
if (plugin.getServerType() == ServerType.CHALLENGE_EVENT) {
if (player != null) {
ChatUtil.sendErrorMessage(player, "Challenge Events können nicht gespeichert werden.");
}
return;
}
isSavingWorld = true;
plugin.getServerConfig().set("saveTime", System.currentTimeMillis());
plugin.saveServerConfig();
Config.setValue("timertime", Challenges.getPlugin().getTimer().getTime());
Config.setValue("backpack_size", Challenges.getPlugin().getBackpack().getSize() / 9);
Challenges.getPlugin().getBackpack().saveInventoryToConfig();
for (Player p : Bukkit.getOnlinePlayers()) {
p.saveData();
}
NMSUtils nms = plugin.getNMSUtils();
if (nms != null) {
for (World world : Bukkit.getWorlds()) {
nms.getWorldUtils().saveWorldNow(world);
}
copyWorldAsync(player, saveID, saveSlot);
}
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Welt konnte nicht gespeichert werden", ex);
}
}
private static void copyWorldAsync(Player player, String saveID, SaveSlot saveSlot) {
File dir = new File(saveID);
File configs = new File("plugins/Challenges");
File end = new File("world_the_end");
File nether = new File("world_nether");
File world = new File("world");
File saveend = new File(saveID + "/world_the_end");
File savenether = new File(saveID + "/world_nether");
File saveworld = new File(saveID + "/world");
File saveconfigs = new File(saveID + "/Challenges");
File saveFolder;
if (plugin.getServerType() != ServerType.ADVENTURE) {
saveFolder = new File(plugin.getChallengeSavePath().toFile(), saveID + "/");
} else {
saveFolder = new File(plugin.getAdventureSavePath().toFile(), saveID + "/");
}
try {
FileUtils.forceMkdir(dir);
FileUtils.copyDirectory(configs, saveconfigs);
FileUtils.copyDirectory(end, saveend);
FileUtils.copyDirectory(nether, savenether);
FileUtils.copyDirectory(world, saveworld);
if (player != null) {
ChatUtil.sendNormalMessage(player, "Welt wurde erfolgreich kopiert und wird nun gespeichert. Dies kann einige Minuten dauern. Du kannst aber ganz normal weiter spielen.");
}
CompletableFuture<Boolean> copyFuture = CompletableFuture.supplyAsync(() -> {
try {
if (!saveFolder.isDirectory()) {
saveFolder.mkdirs();
}
File saveSlotFolder = new File(saveFolder, saveSlot.getSlot());
if (saveSlotFolder.isDirectory()) {
FileUtils.forceDelete(saveSlotFolder);
}
HashMap<File, File> copyMap = new HashMap<>();
copyMap.put(dir, saveSlotFolder);
moveFolderWithProgress(copyMap);
return true;
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, "Could not save world ", ex);
return false;
}
});
copyFuture.thenAccept(result -> {
plugin.getServer().getScheduler().runTask(plugin, () -> {
plugin.getLogger().info("Save result: " + result);
if (result) {
if (player != null) {
ChatUtil.sendNormalMessage(player, "Welt wurde erfolgreich auf Slot " + saveSlot.getSlot() + " gespeichert!");
}
if (isServerRestartRequested) {
restartServerInternal(restartSeed);
} else if (restart) {
Config.setValue("World_Reset", true);
Bukkit.shutdown();
}
} else {
if (player != null) {
ChatUtil.sendErrorMessage(Bukkit.getPlayer(saveID), "Map konnte nicht richtig gespeichert werden, bitte versuche es noch einmal");
}
}
isSavingWorld = false;
try {
FileUtils.deleteDirectory(dir);
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, "Could not delete tempFolder ", ex);
}
});
});
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, "Could not save world ", ex);
}
}
public static void restartServer(@Nullable String seed) {
if (isSavingWorld) {
restartSeed = seed;
isServerRestartRequested = true;
} else {
restartServerInternal(seed);
}
}
private static void restartServerInternal(@Nullable String seed) {
plugin.setWaitingForShutdown(true);
Bukkit.getPluginManager().callEvent(new EventStatusChangedEvent(false));
Bukkit.getPluginManager().callEvent(new ChallengeEventStatusChangedEvent(false));
Bukkit.getPluginManager().callEvent(new ServerStatusChangedEvent(false));
for (Player p : Bukkit.getOnlinePlayers()) {
plugin.portPlayerToLobby(p);
}
Config.setValue("editsettings", true, false);
setSeedInServerProperties(seed);
plugin.getTimer().stopTimer();
for (String key : Config.getConfigurationSection("Saved_Locations").getKeys(false)) {
Config.setValue("Saved_Locations." + key, null, false);
}
plugin.saveConfig();
plugin.getBackpack().saveInventoryToConfig();
if (Config.getBoolean("firsttimerstart") && plugin.getFirstEditor() != null && plugin.getServerType() != ServerType.CHALLENGE_EVENT) {
restart = true;
SaveWorldUtils.saveWorld(plugin.getFirstEditor().getUniqueId().toString(), SaveSlot.SLOT_AUTO);
} else {
Config.setValue("World_Reset", true);
Bukkit.shutdown();
}
}
private static void setSeedInServerProperties(@Nullable String seed) {
try {
BufferedReader in = new BufferedReader(new FileReader("server.properties"));
Properties props = new Properties();
props.load(in);
in.close();
if (seed != null) {
props.setProperty("level-seed", seed);
Config.setValue("resetwithseed", true, false);
} else {
props.setProperty("level-seed", "");
}
FileOutputStream out = new FileOutputStream("server.properties");
props.store(out, null);
out.close();
} catch (IOException e) {
plugin.getLogger().log(Level.SEVERE, "Error while read server properties Config", e);
}
}
public static void moveFolderWithProgress(HashMap<File, File> copyMap) throws IOException {
isCopyWorld = true;
final long[] totalBytes = {0};
final long[] copiedBytes = {0};
for (File sourceFolder : copyMap.keySet()) {
Files.walk(sourceFolder.toPath()).forEach(source -> {
if (!Files.isDirectory(source)) {
totalBytes[0] += source.toFile().length();
}
});
}
for (File sourceFolder : copyMap.keySet()) {
Files.walk(sourceFolder.toPath()).forEach(source -> {
try {
File target = new File(copyMap.get(sourceFolder).toPath().resolve(sourceFolder.toPath().relativize(source)).toString());
if (Files.isDirectory(source)) {
target.mkdir();
} else {
Files.copy(source, target.toPath(), StandardCopyOption.REPLACE_EXISTING);
copiedBytes[0] += source.toFile().length();
progress = (double) copiedBytes[0] / totalBytes[0] * 100;
}
} catch (IOException e) {
plugin.getLogger().log(Level.SEVERE, "Error while copy files", e);
}
});
}
isCopyWorld = false;
}
public static Component getProgressBar(double value) {
int progress = (int) (value / 100 * 40);
Component progressBar = Component.empty();
progressBar = progressBar.append(Component.text("[", ChatUtil.RED));
for (int i = 0; i < 40; i++) {
if (i < progress) {
progressBar = progressBar.append(Component.text("|", ChatUtil.PINK));
} else {
progressBar = progressBar.append(Component.text("|", ChatUtil.GREEN));
}
}
progressBar = progressBar.append(Component.text("] ", ChatUtil.RED).append(Component.text(String.format("%.2f", value) + "%", ChatUtil.PINK)));
return progressBar;
}
}