From 09a45ee2fb4f4babdc6ec4a1c875af891b4897a0 Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 15 Jul 2026 15:44:37 -0400 Subject: [PATCH] Fixed mysterious missing conveyors in schematics / Better schematics import handling --- .../mindustry/android/AndroidLauncher.java | 32 ++------- core/src/mindustry/ClientLauncher.java | 67 ++++++++++++------- core/src/mindustry/game/Schematics.java | 29 +++++++- .../ui/dialogs/SchematicsDialog.java | 16 ++++- ios/src/mindustry/ios/IOSLauncher.java | 41 +++--------- 5 files changed, 98 insertions(+), 87 deletions(-) diff --git a/android/src/mindustry/android/AndroidLauncher.java b/android/src/mindustry/android/AndroidLauncher.java index 74c9df8ae3..cceb0e9148 100644 --- a/android/src/mindustry/android/AndroidLauncher.java +++ b/android/src/mindustry/android/AndroidLauncher.java @@ -17,8 +17,6 @@ import arc.util.*; import dalvik.system.*; import mindustry.*; import mindustry.game.EventType.*; -import mindustry.game.Saves.*; -import mindustry.io.*; import mindustry.net.*; import mindustry.ui.*; import mindustry.ui.FileChooser.*; @@ -298,6 +296,11 @@ public class AndroidLauncher extends AndroidApplication{ if(uri != null){ String scheme = uri.getScheme(); + //clear data (not sure if necessary?) + intent.setAction(Intent.ACTION_MAIN); + intent.setData(null); + setIntent(intent); + if("mindustry".equalsIgnoreCase(scheme)){ //open a server URL String host = uri.getHost(); @@ -309,32 +312,11 @@ public class AndroidLauncher extends AndroidApplication{ }); } }else{ //open a save file - Fi file = Core.files.cache("temp-save." + saveExtension); + Fi file = Core.files.cache("imported"); file.write(getContentResolver().openInputStream(uri), false); - //may run on a different thread - Core.app.post(() -> { - try{ - SaveMeta meta = SaveIO.getMeta(file); - if(!meta.isMap()){ //open save - SaveSlot slot = control.saves.importSave(file); - ui.load.runLoadSave(slot); - }else{ //open map - if(!ui.maps.isShown()){ - ui.maps.show(); - } - ui.maps.tryImportMap(file, result -> ui.maps.showMap(result)); - } - }catch(Throwable e){ - Log.err("Failed to load save", e); - ui.showException("@save.import.invalid", e); - } - }); + ClientLauncher.handleFileImport(file); } - - //clear data (not sure if necessary?) - intent.setAction(Intent.ACTION_MAIN); - intent.setData(null); } }catch(Throwable e){ Log.err(e); diff --git a/core/src/mindustry/ClientLauncher.java b/core/src/mindustry/ClientLauncher.java index 6ae3090410..6f0a8ea55f 100644 --- a/core/src/mindustry/ClientLauncher.java +++ b/core/src/mindustry/ClientLauncher.java @@ -6,17 +6,17 @@ import arc.assets.loaders.*; import arc.audio.*; import arc.files.*; import arc.graphics.*; +import arc.graphics.Texture.*; import arc.graphics.g2d.*; import arc.math.*; import arc.util.*; -import arc.util.io.*; import mindustry.ai.*; import mindustry.audio.*; import mindustry.core.*; import mindustry.ctype.*; import mindustry.game.EventType.*; -import mindustry.game.*; import mindustry.game.Saves.*; +import mindustry.game.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.io.*; @@ -25,9 +25,6 @@ import mindustry.mod.*; import mindustry.net.*; import mindustry.ui.*; -import java.io.*; -import java.util.zip.*; - import static arc.Core.*; import static mindustry.Vars.*; @@ -189,6 +186,8 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform assets.loadRun("contentinit", ContentLoader.class, () -> content.init(), () -> content.load()); assets.loadRun("baseparts", BaseRegistry.class, () -> {}, () -> bases.load()); + + Core.assets.load("sprites/schematic-background.png", Texture.class).loaded = t -> t.setWrap(TextureWrap.repeat); } @Override @@ -314,32 +313,48 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform @Override public void fileDropped(Fi file){ - if(OS.isIos) return; + if(OS.isIos || OS.isAndroid) return; - if(file.extension().equalsIgnoreCase(saveExtension)){ //open save + if(file.extension().equalsIgnoreCase(saveExtension) || file.extension().equalsIgnoreCase(schematicExtension)){ + handleFileImport(file); + } + } + + public static void runOnClientLoad(Runnable run){ + if(clientLoaded){ + run.run(); + }else{ + Events.on(ClientLoadEvent.class, e -> run.run()); + } + } + + /** Can be called from any thread. The file must exist and not have any permission nonsense guarding it. */ + public static void handleFileImport(Fi file){ + Core.app.post(() -> { try{ - if(SaveIO.isSaveValid(file)){ - SaveMeta meta = SaveIO.getMeta(new DataInputStream(new InflaterInputStream(file.read(Streams.defaultBufferSize)))); - if(meta.tags.containsKey("name")){ - //is map - if(!ui.editor.isShown()){ - ui.editor.show(); - } - - ui.editor.beginEditMap(file); - }else if(meta.rules.sector == null){ //don't allow importing campaign saves, they are broken - SaveSlot slot = control.saves.importSave(file); - ui.load.runLoadSave(slot); - }else{ - ui.showErrorMessage("@save.nocampaign"); - } + if(Schematics.isSchematic(file)){ + ui.schematics.show(); + ui.schematics.importAndShow(file); }else{ - ui.showErrorMessage("@save.import.invalid"); + SaveMeta meta = SaveIO.getMeta(file); + if(!meta.isMap()){ //open save + if(meta.rules.sector == null){ + SaveSlot slot = control.saves.importSave(file); + ui.load.runLoadSave(slot); + }else{ + ui.showErrorMessage("@save.nocampaign"); + } + }else{ //open map + if(!ui.maps.isShown()){ + ui.maps.show(); + } + ui.maps.tryImportMap(file, result -> ui.maps.showMap(result)); + } } }catch(Throwable e){ - ui.showException("@save.import.fail", e); + Log.err("Failed to import file", e); + ui.showException("@save.import.invalid", e); } - } - + }); } } diff --git a/core/src/mindustry/game/Schematics.java b/core/src/mindustry/game/Schematics.java index f7e2193588..b5ddcf8b3e 100644 --- a/core/src/mindustry/game/Schematics.java +++ b/core/src/mindustry/game/Schematics.java @@ -247,7 +247,15 @@ public class Schematics implements Loadable{ Draw.rect(Tmp.tr1, buffer.getWidth()/2f, buffer.getHeight()/2f, buffer.getWidth(), -buffer.getHeight()); Draw.color(); - Seq plans = schematic.tiles.map(t -> new BuildPlan(t.x, t.y, t.rotation, t.block, t.config)); + Seq plans = schematic.tiles.map(t -> new BuildPlan(t.x, t.y, t.rotation, t.block, t.config){ + @Override + public Tile tile(){ + //fake tile to return for previews to work properly + emptyTile.x = (short)x; + emptyTile.y = (short)y; + return emptyTile; + } + }); Draw.flush(); //scale each plan to fit schematic @@ -264,6 +272,10 @@ public class Schematics implements Loadable{ plans.each(req -> req.block.drawPlanConfigTop(req, eachPlans)); + //reset state + emptyTile.x = 0; + emptyTile.y = 0; + Draw.flush(); Draw.trans().idt(); @@ -522,6 +534,21 @@ public class Schematics implements Loadable{ //region IO methods + public static boolean isSchematic(Fi file){ + try{ + try(InputStream input = file.read()){ + for(byte b : header){ + if(input.read() != b){ + return false; + } + } + } + return true; + }catch(Throwable t){ + return false; + } + } + /** Loads a schematic from base64. May throw an exception. */ public static Schematic readBase64(String schematic){ try{ diff --git a/core/src/mindustry/ui/dialogs/SchematicsDialog.java b/core/src/mindustry/ui/dialogs/SchematicsDialog.java index 6e0fe6a1b9..657d385e1f 100644 --- a/core/src/mindustry/ui/dialogs/SchematicsDialog.java +++ b/core/src/mindustry/ui/dialogs/SchematicsDialog.java @@ -4,7 +4,6 @@ import arc.*; import arc.files.*; import arc.func.*; import arc.graphics.*; -import arc.graphics.Texture.*; import arc.graphics.g2d.*; import arc.input.*; import arc.math.*; @@ -41,7 +40,6 @@ public class SchematicsDialog extends BaseDialog{ public SchematicsDialog(){ super("@schematics"); - Core.assets.load("sprites/schematic-background.png", Texture.class).loaded = t -> t.setWrap(TextureWrap.repeat); tags = Core.settings.getJson("schematic-tags", Seq.class, String.class, Seq::new); @@ -284,6 +282,20 @@ public class SchematicsDialog extends BaseDialog{ dialog.show(); } + public void importAndShow(Fi file){ + try{ + Schematic s = Schematics.read(file); + s.removeSteamID(); + schematics.add(s); + checkTags(s); + + setup(); + showInfo(s); + }catch(Exception e){ + ui.showException(e); + } + } + public void showExport(Schematic s){ BaseDialog dialog = new BaseDialog("@editor.export"); dialog.cont.pane(p -> { diff --git a/ios/src/mindustry/ios/IOSLauncher.java b/ios/src/mindustry/ios/IOSLauncher.java index 6aa59234a4..10dbcb2bfb 100644 --- a/ios/src/mindustry/ios/IOSLauncher.java +++ b/ios/src/mindustry/ios/IOSLauncher.java @@ -7,11 +7,8 @@ import arc.files.*; import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; -import arc.util.io.*; import mindustry.*; import mindustry.game.EventType.*; -import mindustry.game.Saves.*; -import mindustry.io.*; import mindustry.net.*; import mindustry.ui.FileChooser.*; import mindustry.ui.*; @@ -21,9 +18,7 @@ import org.robovm.apple.uikit.*; import org.robovm.objc.block.*; import rhino.*; -import java.io.*; import java.util.*; -import java.util.zip.*; import static mindustry.Vars.*; import static org.robovm.apple.foundation.NSPathUtilities.*; @@ -224,35 +219,15 @@ public class IOSLauncher extends IOSApplication.Delegate{ } void openURL(NSURL url){ + if(url == null) return; + try{ + Fi file = new Fi(getDocumentsDirectory()).child(url.getLastPathComponent()); + new Fi(url.getPath()).copyTo(file); - Core.app.post(() -> Core.app.post(() -> { - Fi file = Core.files.absolute(getDocumentsDirectory()).child(url.getLastPathComponent()); - Core.files.absolute(url.getPath()).copyTo(file); - - if(file.extension().equalsIgnoreCase(saveExtension)){ //open save - - try{ - if(SaveIO.isSaveValid(file)){ - SaveMeta meta = SaveIO.getMeta(new DataInputStream(new InflaterInputStream(file.read(Streams.defaultBufferSize)))); - if(meta.tags.containsKey("name")){ - //is map - if(!ui.editor.isShown()){ - ui.editor.show(); - } - - ui.editor.beginEditMap(file); - }else{ - SaveSlot slot = control.saves.importSave(file); - ui.load.runLoadSave(slot); - } - }else{ - ui.showErrorMessage("@save.import.invalid"); - } - }catch(Throwable e){ - ui.showException("@save.import.fail", e); - } - } - })); + ClientLauncher.runOnClientLoad(() -> ClientLauncher.handleFileImport(file)); + }catch(Exception e){ + Log.err(e); + } } public static void main(String[] argv){