package game.gui.world; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Date; import java.util.Set; import game.collect.Sets; import game.Game; import game.Game.FileMode; import game.color.TextColor; import game.dimension.Dimension; import game.gui.GuiConfirm; import game.gui.GuiMenu; import game.gui.element.ActButton; import game.gui.element.ActButton.Mode; import game.gui.element.GuiList; import game.gui.element.ListEntry; import game.gui.element.NavButton; import game.gui.world.GuiEdit.Callback; import game.init.Config; import game.init.UniverseRegistry; import game.log.Log; import game.nbt.NBTLoader; import game.nbt.NBTTagCompound; import game.renderer.Drawing; import game.util.CharValidator; import game.util.FileCallback; import game.util.FileUtils; import game.window.Window; import game.world.Converter; import game.world.Converter.SaveVersion; import game.world.Region; import game.world.Region.FolderInfo; import game.world.World; public class GuiWorlds extends GuiList implements ActButton.Callback { protected class SaveInfo implements Comparable, ListEntry { private final String file; private final String name; private final long seed; private final FolderInfo info; public SaveInfo(String file, String name, long seed, FolderInfo info) { this.file = file; this.name = name; this.seed = seed; this.info = info; } public String getFile() { return this.file; } public String getName() { return this.name == null ? "" : this.name; } public String getUser() { return this.info.user == null ? "" : this.info.user; } public String getUsername() { return this.info.user; } public boolean mustConvert() { return this.info.legacy != null; } public String getVersion() { return this.info.legacy == null ? (this.info.version == null ? "" : this.info.version) : this.info.legacy.toString(); } public boolean isIncompatible() { return this.info.legacy == SaveVersion.RELEASE_1_13; } public long getLastPlayed() { return this.info.lastPlayed; } public long getSeed() { return this.seed; } public int compareTo(SaveInfo comp) { return this.info.lastPlayed < comp.info.lastPlayed ? 1 : (this.info.lastPlayed > comp.info.lastPlayed ? -1 : this.file.compareTo(comp.file)); } public void select(boolean isDoubleClick, int mouseX, int mouseY) { boolean use = !this.isIncompatible(); boolean cur = use && !this.mustConvert(); GuiWorlds.this.selectButton.setText(!use || !cur ? "Konvertieren" : "Welt spielen"); GuiWorlds.this.selectButton.enabled = use; GuiWorlds.this.deleteButton.enabled = true; GuiWorlds.this.pruneButton.enabled = cur; GuiWorlds.this.copyButton.enabled = use; GuiWorlds.this.moveButton.enabled = use; GuiWorlds.this.seedButton.enabled = cur; GuiWorlds.this.userButton.enabled = cur; GuiWorlds.this.dupeButton.enabled = cur; if (isDoubleClick && use) { GuiWorlds.this.playWorld(null); } } public void draw(int x, int y, int mouseXIn, int mouseYIn, boolean hover) { Drawing.drawText((this.isIncompatible() ? TextColor.DRED : "") + this.getFile() + (this.mustConvert() ? "" : (TextColor.GRAY + " - " + TextColor.RESET + this.getUser())), x + 2, y, 0xffffffff); Drawing.drawText((this.mustConvert() ? (this.isIncompatible() ? TextColor.CRIMSON : "") + this.getVersion() : this.getName()) , x + 2, y + 18, 0xff808080); Drawing.drawText(this.mustConvert() ? (this.isIncompatible() ? TextColor.CRIMSON + "Kann nicht konvertiert werden!" : "Muss konvertiert werden!") : ( // "Kreativmodus: " + (info.isNoCreative() ? "Aus" : "An") + "Zuletzt gespielt: " + DATE_FORMAT.format(new Date(this.getLastPlayed()))) + " " + TextColor.LGRAY + this.getVersion(), x + 2, y + 18 + 16, 0xff808080); } } public static final GuiWorlds INSTANCE = new GuiWorlds(); public static final Set DISALLOWED_CHARS = Sets.newHashSet('/', '\n', '\r', '\t', '\u0000', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':'); public static final CharValidator VALID_FILE = new CharValidator() { public boolean valid(char ch) { return !DISALLOWED_CHARS.contains(ch); } }; private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); private boolean starting; private int warningTimer; private String warningMessage; private ActButton deleteButton; private ActButton pruneButton; private ActButton selectButton; private ActButton copyButton; private ActButton moveButton; private ActButton seedButton; private ActButton userButton; private ActButton dupeButton; private ActButton createButton; private GuiWorlds() { } public void init(int width, int height) { super.init(width, height); this.starting = false; this.setDimensions(width, height, 32, height - 60); boolean create = true; this.elements.clear(); try { Region.SAVE_DIR.mkdirs(); if(!Region.SAVE_DIR.exists() || !Region.SAVE_DIR.isDirectory()) throw new RuntimeException("Kann den Speicherordner für Welten nicht lesen oder öffnen!"); // java.util.List list = Lists.newArrayList(); File[] files = Region.SAVE_DIR.listFiles(); if(files == null) throw new RuntimeException("Kann den Speicherordner für Welten nicht lesen oder öffnen!"); for(File file : files) { if(!file.isDirectory()) continue; FolderInfo info = Region.loadWorldInfo(file); if(info == null) info = Converter.convertMapFormat(file, null); if(info == null) { this.elements.add(new SaveInfo(file.getName(), null, 0L, new FolderInfo(World.START_TIME, null, file.lastModified(), null, null))); continue; } Dimension dim = info.legacy != null ? null : UniverseRegistry.getDimension(Config.spawnDim); if(dim != null) { File dat = new File(new File(new File(new File(Region.SAVE_DIR, file.getName()), "chunk"), dim.getDimensionName()), "data.nbt"); try { dim.fromNbt(NBTLoader.readGZip(dat).getCompoundTag("Generator")); } catch(Exception e) { } } this.elements.add(new SaveInfo(file.getName(), dim == null ? null : TextColor.stripCodes(dim.getFormattedName(true)), dim == null ? 0L : dim.getSeed(), info)); } // this.saveList = list; Collections.sort(this.elements); } catch (Exception e) { Log.IO.error("Konnte Weltliste nicht laden", e); this.elements.clear(); create = false; this.warningTimer = 120; this.warningMessage = "Welten-Ordner nicht lesbar!"; } this.add(this.selectButton = new ActButton(width / 2 - 383, height - 56, 150, 24, this, "Welt spielen")); this.add(this.createButton = new ActButton(width / 2 + 233, height - 56, 150, 24, this, (create ? "" : "" + TextColor.DRED) + (create ? "Neue Welt ..." : "Fehler!"))); this.add(this.deleteButton = new ActButton(width / 2 - 229, height - 28, 150, 24, this, "Löschen")); this.add(this.pruneButton = new ActButton(width / 2 - 75, height - 28, 150, 24, this, "Leeren")); this.add(this.copyButton = new ActButton(width / 2 - 383, height - 28, 150, 24, this, "Kopieren")); this.add(this.moveButton = new ActButton(width / 2 + 79, height - 28, 150, 24, this, "Verschieben")); this.add(this.seedButton = new ActButton(width / 2 - 75, height - 56, 150, 24, this, "Startwert")); this.add(this.userButton = new ActButton(width / 2 - 229, height - 56, 150, 24, this, "Spieler")); this.add(this.dupeButton = new ActButton(width / 2 + 79, height - 56, 150, 24, this, "Duplizieren")); this.add(new NavButton(width / 2 + 233, height - 28, 150, 24, GuiMenu.INSTANCE, "Abbrechen")); this.add(new ActButton(4, 4, 200, 24, new ActButton.Callback() { public void use(ActButton elem, ActButton.Mode action) { if(GuiWorlds.this.gm.theWorld != null) return; GuiWorlds.this.gm.showDirDialog(FileMode.DIRECTORY_LOAD, "Welt öffnen", Region.SAVE_DIR, new FileCallback() { public void selected(File file) { if(GuiWorlds.this.gm.theWorld == null && GuiWorlds.this.gm.open instanceof GuiWorlds) GuiWorlds.this.gm.startServer(file, "sen"); } }); } }, "Welt laden")); this.selectButton.enabled = false; this.deleteButton.enabled = false; this.pruneButton.enabled = false; this.copyButton.enabled = false; this.moveButton.enabled = false; this.seedButton.enabled = false; this.userButton.enabled = false; this.dupeButton.enabled = false; this.createButton.enabled = create; } public String getTitle() { return "Welt auswählen"; } public int getListWidth() { return 660; } public int getSlotHeight() { return 56; } public void updateScreen() { super.updateScreen(); // this.userField.updateCursorCounter(); if(this.warningTimer > 0) { if(--this.warningTimer == 0) { this.warningMessage = null; } } } // /** // * Handles mouse input. // */ // public void handleMouseInput() throws IOException // { // super.handleMouseInput(); // this.availableWorlds.handleMouseInput(); // } // private void loadLevelList() { // // } private String getSaveAt() { return this.getSelected().getFile(); } // protected String getNameAt(int index) // { // String s = ((SaveInfo)this.saveList.get(index)).getName(); // // if (s == null || s.isEmpty()) // { // s = I18n.format("selectWorld.world") + " " + (index + 1); // } // // return s; // } // public void addWorldSelectionButtons(boolean create) // { //// this.userField = new TextField(this.width / 2 + 4 + 2, 26, 146, 16); //// this.userField.setMaxStringLength(16); //// this.userField.setText(this.gm.localUser); //// this.userField.setValidator(new Predicate() //// { //// public boolean test(String name) //// { //// return StringValidator.isValidUser(name); //// } //// }); // } public void use(ActButton button, Mode mode) { // if (button.enabled) // { if (button == this.deleteButton) { String s = this.getSaveAt(); if (s != null) { GuiConfirm guiyesno = makeYesNo(this, s, false); this.gm.displayGuiScreen(guiyesno); } } else if (button == this.pruneButton) { String s = this.getSaveAt(); if (s != null) { GuiConfirm guiyesno = makeYesNo(this, s, true); this.gm.displayGuiScreen(guiyesno); } } else if (button == this.selectButton) { // if(isShiftKeyDown()) // // else this.playWorld(null); } else if (button == this.createButton) { if(GuiWorlds.this.gm.theWorld == null) { if(this.gm.shift()) { // this.gm.displayGuiScreen(null); this.gm.startServer(null, "debug"); } else { this.gm.displayGuiScreen(GuiCreate.INSTANCE); } } } else if (button == this.userButton) { this.gm.displayGuiScreen(new GuiEdit(this.getSelected().getUsername(), true, "Spieler für '" + this.getSaveAt() + "' ändern", "Ändern", "Spielername", new Callback() { public void confirm(String name) { if(name != null) GuiWorlds.this.changeUser(name); GuiWorlds.this.gm.displayGuiScreen(GuiWorlds.this); } })); } // else if (button.id == 0) // { // this.gm.displayGuiScreen(this.parentScreen); // } else if (button == this.dupeButton) { } else if (button == this.copyButton) { this.gm.displayGuiScreen(new GuiEdit("", false, "Welt '" + this.getSaveAt() + "' kopieren", "Kopieren", "Ordner der Welt", new Callback() { public void confirm(String name) { if(name != null) GuiWorlds.this.copyWorld(name); GuiWorlds.this.gm.displayGuiScreen(GuiWorlds.this); } })); } else if (button == this.moveButton) { this.gm.displayGuiScreen(new GuiEdit(this.getSaveAt(), false, "Welt '" + this.getSaveAt() + "' verschieben", "Verschieben", "Ordner der Welt", new Callback() { public void confirm(String name) { if(name != null) GuiWorlds.this.moveWorld(name); GuiWorlds.this.gm.displayGuiScreen(GuiWorlds.this); } })); } else if (button == this.seedButton) { Window.setClipboard("" + this.getSelected().getSeed()); this.warningTimer = 40; this.warningMessage = TextColor.DGREEN + "Startwert wurde in die Zwischenablage kopiert"; } // else // { // this.availableWorlds.actionPerformed(button); // } // } } private void copyWorld(String name) { File oldDir = new File(Region.SAVE_DIR, this.getSaveAt()); // String name = this.getSaveAt(this.selectedIndex); // while(new File(Region.SAVE_DIR, name).exists()) { // name = name + "-"; // } File newDir = new File(Region.SAVE_DIR, name); Log.IO.info("Kopiere Welt " + oldDir + " nach " + newDir); if(!copyFiles(oldDir, newDir, oldDir.listFiles())) { this.warningTimer = 120; this.warningMessage = "Fehler beim Kopieren der Welt, diese könnte unvollständig sein!"; } // try // { // this.loadLevelList(); // } // catch (Exception anvilconverterexception) // { // Log.error((String)"Konnte Weltliste nicht laden", (Throwable)anvilconverterexception); // } this.gm.displayGuiScreen(this); } private void moveWorld(String dest) { File oldDir = new File(Region.SAVE_DIR, this.getSaveAt()); File newDir = new File(Region.SAVE_DIR, dest); Log.IO.info("Kopiere Welt " + oldDir + " nach " + newDir); if(!copyFiles(oldDir, newDir, oldDir.listFiles())) { this.warningTimer = 120; this.warningMessage = "Fehler beim Kopieren der Welt, diese könnte unvollständig sein!"; return; } if(!this.deleteWorld(false)) { this.warningTimer = 120; this.warningMessage = "Fehler beim Löschen der Welt!"; } this.gm.displayGuiScreen(this); } private void changeUser(String user) { File file = new File(new File(Region.SAVE_DIR, this.getSaveAt()), "level.nbt"); if(file.exists()) { try { NBTTagCompound tag = NBTLoader.readGZip(file); tag.setString("Owner", user); NBTLoader.writeGZip(tag, file); } catch(Exception e) { Log.IO.error("Fehler beim Verarbeiten von " + file, e); } } // this.gm.displayGuiScreen(this); } private void playWorld(String user) { if(GuiWorlds.this.gm.theWorld != null) return; // this.gm.displayGuiScreen(null); if (!this.starting) { this.starting = true; // int index = this.selectedIndex; if(user == null) user = this.getSelected().getUsername(); String dir = this.getSaveAt(); File folder = new File(Region.SAVE_DIR, dir); if(folder.isDirectory()) { if(user == null) { this.gm.displayGuiScreen(new GuiEdit("", true, "Spieler für '" + dir + "' festlegen", "Festlegen", "Spielername", new Callback() { public void confirm(String name) { if(name != null) GuiWorlds.this.playWorld(name); else GuiWorlds.this.gm.displayGuiScreen(GuiWorlds.this); } })); this.starting = false; } else { if(this.getSelected().mustConvert()) { Game r = this.gm; Converter.convertMapFormat(folder, user); this.starting = false; } else { this.gm.startServer(folder, user); } } } } } // public void confirmClicked(boolean result) // { // if (this.confirmingDelete >= 0 || this.confirmingPrune >= 0) // { // boolean prune = this.confirmingPrune >= 0; // int id = prune ? this.confirmingPrune : this.confirmingDelete; // this.confirmingDelete = -1; // this.confirmingPrune = -1; // // if (result) // { //// Region.clearCache(true); // if(!this.deleteWorld(prune)) { // this.warningTimer = 120; // this.warningMessage = "Fehler beim " + (prune ? "Leeren" : "Löschen") + " der Welt!"; // } // //// try //// { //// this.loadLevelList(); //// } //// catch (Exception anvilconverterexception) //// { //// Log.error((String)"Konnte Weltliste nicht laden", (Throwable)anvilconverterexception); //// } // } // // this.gm.displayGuiScreen(this); // } // } private boolean deleteWorld(final boolean pruneOnly) { File worldDir = new File(Region.SAVE_DIR, this.getSaveAt()); if (!worldDir.exists()) { return true; } Log.IO.info((pruneOnly ? "Leere" : "Lösche") + " Welt " + worldDir); boolean flag = false; for (int i = 1; i <= 5; ++i) { Log.IO.info("Versuch " + i + "..."); if (FileUtils.deleteFiles(worldDir.listFiles(new FileFilter() { public boolean accept(File file) { return !pruneOnly || (file.isDirectory() ? file.getName().equals("chunk") : file.getName().equals("signs.nbt")); } }))) { flag = true; break; } Log.IO.warn("Konnte Inhalt nicht löschen."); if (i < 5) { try { Thread.sleep(500L); } catch (InterruptedException e) { ; } } } return pruneOnly ? flag : worldDir.delete(); } private static boolean copyFiles(File source, File dest, File[] files) { dest.mkdirs(); if(files == null || !dest.isDirectory()) { Log.IO.warn("Konnte Ordner " + source + " nicht nach " + dest + " kopieren"); return false; } boolean flag = true; for (int i = 0; i < files.length; ++i) { File file = files[i]; File nfile = new File(dest, file.getName()); Log.IO.info("Kopiere " + file + " nach " + nfile); if (file.isDirectory()) { flag &= copyFiles(file, nfile, file.listFiles()); continue; } try { Files.copy(file.toPath(), nfile.toPath(), StandardCopyOption.REPLACE_EXISTING); // Files.copy(file, nfile); } catch(IOException e) { Log.IO.error("Konnte Datei " + file + " nicht nach " + nfile + " kopieren", e); flag = false; } } return flag; } public void drawOverlays() { super.drawOverlays(); if(this.warningMessage != null) { Drawing.drawRectBorder(this.gm.fb_x / 2 - 200, this.gm.fb_y - 85, 400, 20, 0xff000000, 0xff202020, 0xffffffff, 0xffa0a0a0); // drawRect(this.gm.fb_x / 2 - 191, this.gm.fb_y - 82, this.gm.fb_x / 2 + 191, this.gm.fb_y - 66, 0xff808080); // drawRect(this.gm.fb_x / 2 - 190, this.gm.fb_y - 81, this.gm.fb_x / 2 + 190, this.gm.fb_y - 67, 0xff000000); Drawing.drawTextCentered(this.warningMessage, this.gm.fb_x / 2, this.gm.fb_y - 84, 0xffff0000); } } public static GuiConfirm makeYesNo(GuiWorlds selectWorld, String name, boolean prune) { String s = prune ? "Willst du diese Welt wirklich leeren?" : "Bist du sicher, dass du diese Welt löschen möchtest?"; String s1 = "\'" + name + "\' " + (prune ? "wird alle Daten außer dem Spieler und Einstellungen verlieren!" : "wird für immer verloren sein! (Eine lange Zeit!)"); String s2 = prune ? "Leeren" : "Löschen"; String s3 = "Abbrechen"; GuiConfirm guiyesno = new GuiConfirm(new GuiConfirm.Callback() { @Override public void confirm(boolean confirmed) { if(confirmed && !selectWorld.deleteWorld(prune)) { selectWorld.warningTimer = 120; selectWorld.warningMessage = "Fehler beim " + (prune ? "Leeren" : "Löschen") + " der Welt!"; } selectWorld.gm.displayGuiScreen(selectWorld); } }, s, s1, s2, s3); return guiyesno; } }