diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 1d1e6a39fc..4d829e9eb5 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -164,6 +164,11 @@ cancel = Cancel openlink = Open Link copylink = Copy Link back = Back +data.export = Export Data +data.import = Import Data +data.exported = Data exported. +data.invalid = This isn't valid game data. +data.import.confirm = Importing external data will erase[scarlet] all[] your current game data.\n[accent]This cannot be undone![]\n\nOnce the data is imported, your game will exit immediately. classic.export = Export Classic Data classic.export.text = [accent]Mindustry[] has just had a major update.\nClassic (v3.5 build 40) save or map data has been detected. Would you like to export these saves to your phone's home folder, for use in the Mindustry Classic app? quit.confirm = Are you sure you want to quit? @@ -392,6 +397,7 @@ zone.impact0078.description = zone.crags.description = settings.language = Language +settings.data = Game Data settings.reset = Reset to Defaults settings.rebind = Rebind settings.controls = Controls diff --git a/core/src/io/anuke/mindustry/game/GlobalData.java b/core/src/io/anuke/mindustry/game/GlobalData.java index a905f09d39..43b8095937 100644 --- a/core/src/io/anuke/mindustry/game/GlobalData.java +++ b/core/src/io/anuke/mindustry/game/GlobalData.java @@ -2,11 +2,16 @@ package io.anuke.mindustry.game; import io.anuke.arc.*; import io.anuke.arc.collection.*; +import io.anuke.arc.files.*; +import io.anuke.arc.util.io.*; import io.anuke.mindustry.*; import io.anuke.mindustry.content.*; import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.type.*; +import java.io.*; +import java.util.zip.*; + import static io.anuke.mindustry.Vars.*; /** Stores player unlocks. Clientside only. */ @@ -29,6 +34,43 @@ public class GlobalData{ }); } + public void exportData(FileHandle file) throws IOException{ + Array files = new Array<>(); + files.add(Core.settings.getSettingsFile()); + files.addAll(customMapDirectory.list()); + files.addAll(saveDirectory.list()); + String base = Core.settings.getDataDirectory().path(); + + try(OutputStream fos = file.write(false, 2048); ZipOutputStream zos = new ZipOutputStream(fos)){ + for(FileHandle add : files){ + zos.putNextEntry(new ZipEntry(add.path().substring(base.length()))); + Streams.copyStream(add.read(), zos); + zos.closeEntry(); + } + + } + } + + public void importData(FileHandle file){ + FileHandle zipped = new ZipFileHandle(file); + + FileHandle base = Core.settings.getDataDirectory(); + if(!base.child("settings.bin").exists()){ + throw new IllegalArgumentException("Not valid save data."); + } + + //purge existing data + for(FileHandle f : base.list()){ + if(f.isDirectory()){ + f.deleteDirectory(); + }else{ + f.delete(); + } + } + + zipped.walk(f -> f.copyTo(base.child(f.path()))); + } + public void modified(){ modified = true; } diff --git a/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java index 2450e0dd93..08c515a950 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -22,12 +22,13 @@ import static io.anuke.arc.Core.bundle; import static io.anuke.mindustry.Vars.*; public class SettingsMenuDialog extends SettingsDialog{ - public SettingsTable graphics; - public SettingsTable game; - public SettingsTable sound; + private SettingsTable graphics; + private SettingsTable game; + private SettingsTable sound; private Table prefs; private Table menu; + private FloatingDialog dataDialog; private boolean wasPaused; public SettingsMenuDialog(){ @@ -75,6 +76,85 @@ public class SettingsMenuDialog extends SettingsDialog{ prefs.clearChildren(); prefs.add(menu); + dataDialog = new FloatingDialog("$settings.data"); + dataDialog.addCloseButton(); + + dataDialog.cont.table("button", t -> { + t.defaults().size(240f, 60f).left(); + String style = "clear"; + + t.addButton("$settings.cleardata", style, () -> ui.showConfirm("$confirm", "$settings.clearall.confirm", () -> { + ObjectMap map = new ObjectMap<>(); + for(String value : Core.settings.keys()){ + if(value.contains("usid") || value.contains("uuid")){ + map.put(value, Core.settings.getString(value)); + } + } + Core.settings.clear(); + Core.settings.putAll(map); + Core.settings.save(); + + for(FileHandle file : dataDirectory.list()){ + file.deleteDirectory(); + } + + Core.app.exit(); + })); + + t.row(); + + if(android && (Core.files.local("mindustry-maps").exists() || Core.files.local("mindustry-saves").exists())){ + t.addButton("$classic.export", style, () -> { + control.checkClassicData(); + }); + } + + t.row(); + + t.addButton("$data.export", style, () -> { + if(ios){ + FileHandle file = Core.files.local("mindustry-data-export.zip"); + try{ + data.exportData(file); + }catch(Exception e){ + ui.showError(Strings.parseException(e, true)); + } + Platform.instance.shareFile(file); + }else{ + Platform.instance.showFileChooser("$data.export", "Zip Files", file -> { + FileHandle ff = file; + if(!ff.extension().equals("zip")){ + ff = ff.sibling(ff.nameWithoutExtension() + ".zip"); + } + try{ + data.exportData(ff); + ui.showInfo("$data.exported"); + }catch(Exception e){ + e.printStackTrace(); + ui.showError(Strings.parseException(e, true)); + } + }, false, f -> false); + } + }); + + t.row(); + + //iOS doesn't have a file chooser. + if(!ios){ + t.addButton("$data.import", style, () -> ui.showConfirm("$confirm", "$data.import.confirm", () -> Platform.instance.showFileChooser("$data.import", "Zip Files", file -> { + try{ + data.importData(file); + Core.app.exit(); + }catch(IllegalArgumentException e){ + ui.showError("$data.invalid"); + }catch(Exception e){ + e.printStackTrace(); + ui.showError(Strings.parseException(e, true)); + } + }, true, f -> f.equalsIgnoreCase("zip")))); + } + }); + ScrollPane pane = new ScrollPane(prefs); pane.addCaptureListener(new InputListener(){ @Override @@ -121,6 +201,9 @@ public class SettingsMenuDialog extends SettingsDialog{ menu.row(); menu.addButton("$settings.controls", style, ui.controls::show); } + + menu.row(); + menu.addButton("$settings.data", style, () -> dataDialog.show()); } void addSettings(){ @@ -141,31 +224,6 @@ public class SettingsMenuDialog extends SettingsDialog{ game.checkPref("savecreate", true); - game.pref(new Setting(){ - @Override - public void add(SettingsTable table){ - table.addButton("$settings.cleardata", () -> ui.showConfirm("$confirm", "$settings.clearall.confirm", () -> { - ObjectMap map = new ObjectMap<>(); - for(String value : Core.settings.keys()){ - if(value.contains("usid") || value.contains("uuid")){ - map.put(value, Core.settings.getString(value)); - } - } - Core.settings.clear(); - Core.settings.putAll(map); - Core.settings.save(); - - for(FileHandle file : dataDirectory.list()){ - file.deleteDirectory(); - } - - Core.app.exit(); - })).size(220f, 60f).pad(6).left(); - table.add(); - table.row(); - } - }); - game.pref(new Setting(){ @Override public void add(SettingsTable table){ @@ -179,20 +237,6 @@ public class SettingsMenuDialog extends SettingsDialog{ } }); - if(android && (Core.files.local("mindustry-maps").exists() || Core.files.local("mindustry-saves").exists())){ - game.pref(new Setting(){ - @Override - public void add(SettingsTable table){ - table.addButton("$classic.export", () -> { - control.checkClassicData(); - }).size(220f, 60f).pad(6).left(); - table.add(); - table.row(); - hide(); - } - }); - } - graphics.sliderPref("uiscale", 100, 25, 400, 25, s -> { if(ui.settings != null){ Core.settings.put("uiscalechanged", true);