.mmap file importing / Better exception parsing

This commit is contained in:
Anuken
2019-05-31 10:24:51 -04:00
parent 30a254e9be
commit 940965f03d
16 changed files with 119 additions and 66 deletions

View File

@@ -105,6 +105,8 @@ public class Vars{
public static FileHandle screenshotDirectory;
/** data subdirectory used for custom mmaps */
public static FileHandle customMapDirectory;
/** tmp subdirectory for map conversion */
public static FileHandle tmpDirectory;
/** data subdirectory used for saves */
public static FileHandle saveDirectory;
/** old map file extension, for conversion */
@@ -206,5 +208,6 @@ public class Vars{
screenshotDirectory = dataDirectory.child("screenshots/");
customMapDirectory = dataDirectory.child("maps/");
saveDirectory = dataDirectory.child("saves/");
tmpDirectory = dataDirectory.child("tmp/");
}
}

View File

@@ -135,7 +135,7 @@ public class Control implements ApplicationListener{
Net.host(port);
player.isAdmin = true;
}catch(IOException e){
ui.showError(Core.bundle.format("server.error", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("server.error", Strings.parseException(e, true)));
Core.app.post(() -> state.set(State.menu));
}
}

View File

@@ -4,6 +4,7 @@ import io.anuke.arc.Core;
import io.anuke.arc.Input.TextInput;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.function.Consumer;
import io.anuke.arc.function.Predicate;
import io.anuke.arc.math.RandomXS128;
import io.anuke.arc.scene.ui.TextField;
import io.anuke.arc.util.serialization.Base64Coder;
@@ -66,14 +67,14 @@ public abstract class Platform{
}
/**
* Show a file chooser. Desktop only.
* Show a file chooser.
* @param text File chooser title text
* @param content Description of the type of files to be loaded
* @param cons Selection listener
* @param open Whether to open or save files
* @param filetype File extension to filter
*/
public void showFileChooser(String text, String content, Consumer<FileHandle> cons, boolean open, String filetype){
public void showFileChooser(String text, String content, Consumer<FileHandle> cons, boolean open, Predicate<String> filetype){
}
/** Hide the app. Android only. */

View File

@@ -25,8 +25,7 @@ import io.anuke.mindustry.ui.dialogs.*;
import io.anuke.mindustry.ui.fragments.*;
import static io.anuke.arc.scene.actions.Actions.*;
import static io.anuke.mindustry.Vars.control;
import static io.anuke.mindustry.Vars.disableUI;
import static io.anuke.mindustry.Vars.*;
public class UI implements ApplicationListener{
private FreeTypeFontGenerator generator;
@@ -244,8 +243,17 @@ public class UI implements ApplicationListener{
}
public void showError(String text){
new Dialog("$error.title", "dialog"){{
cont.margin(15).add(text).width(400f).wrap().get().setAlignment(Align.center, Align.center);
new Dialog("", "dialog"){{
setFillParent(true);
cont.add("$error.title");
cont.row();
cont.margin(15).pane(t -> {
Label l = t.add(text).pad(14f).get();
l.setAlignment(Align.center, Align.left);
if(mobile){
t.getCell(l).wrap().width(400f);
}
});
buttons.addButton("$ok", this::hide).size(90, 50).pad(4);
}}.show();
}

View File

@@ -21,9 +21,9 @@ import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.io.JsonIO;
import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.io.*;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.ui.dialogs.FileChooser;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
@@ -93,14 +93,16 @@ public class MapEditorDialog extends Dialog implements Disposable{
"$editor.importmap", "$editor.importmap.description", "icon-load-map", (Runnable)loadDialog::show,
"$editor.importfile", "$editor.importfile.description", "icon-file", (Runnable)() ->
Platform.instance.showFileChooser("$editor.loadmap", "Map Files", file -> ui.loadAnd(() -> {
try{
//TODO what if it's an image? users should be warned for their stupidity
editor.beginEdit(MapIO.createMap(file, true));
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
Log.err(e);
}
}), true, mapExtension),
world.maps.tryCatchMapError(() -> {
if(MapIO.isImage(file)){
ui.showInfo("$editor.errorimage");
}else if(file.extension().equalsIgnoreCase(oldMapExtension)){
editor.beginEdit(world.maps.makeLegacyMap(file));
}else{
editor.beginEdit(MapIO.createMap(file, true));
}
});
}), true, FileChooser.anyMapFiles),
"$editor.importimage", "$editor.importimage.description", "icon-file-image", (Runnable)() ->
Platform.instance.showFileChooser("$loadimage", "Image Files", file ->
@@ -110,10 +112,10 @@ public class MapEditorDialog extends Dialog implements Disposable{
editor.beginEdit(pixmap);
pixmap.dispose();
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true)));
Log.err(e);
}
}), true, "png"))
}), true, FileChooser.pngFiles))
);
t.addImageTextButton("$editor.export", "icon-save-map", isize, () ->
@@ -127,11 +129,11 @@ public class MapEditorDialog extends Dialog implements Disposable{
}
MapIO.writeMap(result, editor.createMap(result));
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorsave", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("editor.errorsave", Strings.parseException(e, true)));
Log.err(e);
}
});
}, false, mapExtension));
}, false, FileChooser.mapFiles));
});
menu.cont.row();
@@ -158,7 +160,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
try{
editor.beginEdit(map);
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true)));
Log.err(e);
}
}));
@@ -336,7 +338,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
show();
}catch(Exception e){
Log.err(e);
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true)));
}
});
}

View File

@@ -55,6 +55,9 @@ public class LegacyMapIO{
//meta is uncompressed
int version = stream.readInt();
if(version != 1){
throw new IOException("Outdated legacy map format");
}
int build = stream.readInt();
short width = stream.readShort(), height = stream.readShort();
byte tagAmount = stream.readByte();

View File

@@ -3,12 +3,13 @@ package io.anuke.mindustry.maps;
import io.anuke.arc.Core;
import io.anuke.arc.collection.*;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.function.ExceptionRunnable;
import io.anuke.arc.graphics.Texture;
import io.anuke.arc.util.Disposable;
import io.anuke.arc.util.Log;
import io.anuke.arc.util.*;
import io.anuke.arc.util.serialization.Json;
import io.anuke.mindustry.game.SpawnGroup;
import io.anuke.mindustry.io.*;
import io.anuke.mindustry.io.LegacyMapIO;
import io.anuke.mindustry.io.MapIO;
import java.io.IOException;
import java.io.StringWriter;
@@ -116,6 +117,14 @@ public class Maps implements Disposable{
}
}
/** Creates a legacy map by converting it to a non-legacy map and pasting it in a temp directory.
* Should be followed up by {@link #importMap(FileHandle)} .*/
public Map makeLegacyMap(FileHandle file) throws IOException{
FileHandle dst = tmpDirectory.child("conversion_map." + mapExtension);
LegacyMapIO.convertMap(file, dst);
return MapIO.createMap(dst, true);
}
/** Import a map, then save it. This updates all values and stored data necessary. */
public void importMap(FileHandle file) throws IOException{
FileHandle dest = findFile();
@@ -124,6 +133,24 @@ public class Maps implements Disposable{
loadMap(dest, true);
}
/** Attempts to run the following code;
* catches any errors and attempts to display them in a readable way.*/
public void tryCatchMapError(ExceptionRunnable run){
try{
run.run();
}catch(Exception e){
Log.err(e);
if("Outdated legacy map format".equals(e.getMessage())){
ui.showError("$editor.errorlegacy");
}else if(e.getMessage() != null && e.getMessage().contains("Incorrect header!")){
ui.showError("$editor.errorheader");
}else{
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true)));
}
}
}
/** Removes a map completely. */
public void removeMap(Map map){
if(map.texture != null){

View File

@@ -6,8 +6,7 @@ import io.anuke.arc.collection.*;
import io.anuke.arc.function.BiConsumer;
import io.anuke.arc.function.Consumer;
import io.anuke.arc.net.HttpRequestBuilder;
import io.anuke.arc.util.Log;
import io.anuke.arc.util.Time;
import io.anuke.arc.util.*;
import io.anuke.arc.util.pooling.Pools;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.gen.Call;
@@ -67,7 +66,7 @@ public class Net{
}else if(error.equals("alreadyconnected")){
error = Core.bundle.get("error.alreadyconnected");
}else if(!error.isEmpty()){
error = Core.bundle.get("error.any") + "\n" + t.getClass().getSimpleName() + "\n" + (t.getMessage() == null ? "" : t.getMessage());
error = Core.bundle.get("error.any") + "\n" + Strings.parseException(e, true);
}
ui.showText("", Core.bundle.format("connectfail", error));

View File

@@ -12,6 +12,7 @@ import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.scene.ui.layout.Unit;
import io.anuke.arc.util.*;
import io.anuke.arc.util.pooling.Pools;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.Platform;
import java.util.Arrays;
@@ -31,6 +32,11 @@ public class FileChooser extends FloatingDialog{
private boolean open;
private int lastWidth = Core.graphics.getWidth(), lastHeight = Core.graphics.getHeight();
public static final Predicate<String> pngFiles = str -> str.equals("png");
public static final Predicate<String> anyMapFiles = str -> str.equals(Vars.oldMapExtension) || str.equals(Vars.mapExtension);
public static final Predicate<String> mapFiles = str -> str.equals(Vars.mapExtension);
public static final Predicate<String> saveFiles = str -> str.equals(Vars.saveExtension);
public FileChooser(String title, Predicate<FileHandle> filter, boolean open, Consumer<FileHandle> result){
super(title);
this.open = open;

View File

@@ -56,7 +56,7 @@ public class HostDialog extends FloatingDialog{
Net.host(Vars.port);
player.isAdmin = true;
}catch(IOException e){
ui.showError(Core.bundle.format("server.error", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("server.error", Strings.parseException(e, true)));
}
ui.loadfrag.hide();
hide();

View File

@@ -90,16 +90,16 @@ public class LoadDialog extends FloatingDialog{
slot.exportFile(file);
setup();
}catch(IOException e){
ui.showError(Core.bundle.format("save.export.fail", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("save.export.fail", Strings.parseException(e, true)));
}
}, false, saveExtension);
}, false, FileChooser.saveFiles);
}else{
try{
FileHandle file = Core.files.local("save-" + slot.getName() + "." + Vars.saveExtension);
slot.exportFile(file);
Platform.instance.shareFile(file);
}catch(Exception e){
ui.showError(Core.bundle.format("save.export.fail", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("save.export.fail", Strings.parseException(e, true)));
}
}
}).size(14 * 3).right();
@@ -155,12 +155,12 @@ public class LoadDialog extends FloatingDialog{
setup();
}catch(IOException e){
e.printStackTrace();
ui.showError(Core.bundle.format("save.import.fail", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("save.import.fail", Strings.parseException(e, true)));
}
}else{
ui.showError("$save.import.invalid");
}
}, true, saveExtension);
}, true, FileChooser.saveFiles);
}).fillX().margin(10f).minWidth(300f).height(70f).pad(4f).padRight(-4);
}

View File

@@ -5,7 +5,7 @@ import io.anuke.arc.graphics.Color;
import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.*;
import io.anuke.arc.util.Scaling;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.io.MapIO;
@@ -23,8 +23,19 @@ public class MapsDialog extends FloatingDialog{
addCloseButton();
buttons.addImageTextButton("$editor.importmap", "icon-add", 14 * 2, () -> {
Platform.instance.showFileChooser("$editor.importmap", "Map File", file -> {
try{
Map map = MapIO.createMap(file, true);
world.maps.tryCatchMapError(() -> {
if(MapIO.isImage(file)){
ui.showError("$editor.errorimage");
return;
}
Map map;
if(file.extension().equalsIgnoreCase(mapExtension)){
map = MapIO.createMap(file, true);
}else{
map = world.maps.makeLegacyMap(file);
}
String name = map.tags.get("name");
if(name == null){
ui.showError("$editor.errorname");
@@ -34,27 +45,21 @@ public class MapsDialog extends FloatingDialog{
Map conflict = world.maps.all().find(m -> m.name().equals(name));
if(conflict != null && !conflict.custom){
ui.showError(Core.bundle.format("editor.import.exists", name));
ui.showInfo(Core.bundle.format("editor.import.exists", name));
}else if(conflict != null){
ui.showConfirm("$confirm", "$editor.overwrite.confirm", () -> {
try{
world.maps.tryCatchMapError(() -> {
world.maps.importMap(file);
setup();
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
Log.err(e);
}
});
});
}else{
world.maps.importMap(file);
world.maps.importMap(map.file);
setup();
}
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
Log.err(e);
}
}, true, mapExtension);
});
}, true, FileChooser.anyMapFiles);
}).size(230f, 64f);
shown(this::setup);