From 4497e34bbba092044175b8600cdb686f45489c33 Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 4 Feb 2026 12:36:40 -0500 Subject: [PATCH] Save file drop support for desktop --- android/AndroidManifest.xml | 7 ----- core/src/mindustry/ClientLauncher.java | 36 +++++++++++++++++++++++ core/src/mindustry/mod/ContentParser.java | 9 ++++++ core/src/mindustry/mod/DataPatcher.java | 7 +++++ gradle.properties | 2 +- tests/src/test/java/PatcherTests.java | 20 +++++++++++++ 6 files changed, 73 insertions(+), 8 deletions(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index cca7d06d82..4078c59538 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -32,13 +32,6 @@ - - - - - - - diff --git a/core/src/mindustry/ClientLauncher.java b/core/src/mindustry/ClientLauncher.java index 43502cfc3a..6a4734dfce 100644 --- a/core/src/mindustry/ClientLauncher.java +++ b/core/src/mindustry/ClientLauncher.java @@ -9,19 +9,25 @@ import arc.graphics.*; 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.gen.*; import mindustry.graphics.*; +import mindustry.io.*; import mindustry.maps.*; import mindustry.mod.*; import mindustry.net.*; import mindustry.ui.*; +import java.io.*; +import java.util.zip.*; + import static arc.Core.*; import static mindustry.Vars.*; @@ -303,4 +309,34 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform super.pause(); } } + + @Override + public void fileDropped(Fi file){ + if(!OS.isIos){ + 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 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"); + } + }else{ + ui.showErrorMessage("@save.import.invalid"); + } + }catch(Throwable e){ + ui.showException("@save.import.fail", e); + } + } + } + } } diff --git a/core/src/mindustry/mod/ContentParser.java b/core/src/mindustry/mod/ContentParser.java index d6fa9fcf5f..f801fe7ddf 100644 --- a/core/src/mindustry/mod/ContentParser.java +++ b/core/src/mindustry/mod/ContentParser.java @@ -43,6 +43,7 @@ import mindustry.type.*; import mindustry.type.ammo.*; import mindustry.type.weather.*; import mindustry.world.*; +import mindustry.world.blocks.*; import mindustry.world.blocks.units.*; import mindustry.world.blocks.units.UnitFactory.*; import mindustry.world.consumers.*; @@ -89,6 +90,14 @@ public class ContentParser{ if(Attribute.exists(attr)) return Attribute.get(attr); return Attribute.add(attr); }); + put(Attributes.class, (type, data) -> { + Attributes attr = new Attributes(); + for(var child : data){ + Attribute value = Attribute.exists(child.name) ? Attribute.get(child.name) : Attribute.add(child.name); + attr.set(value, child.asFloat()); + } + return attr; + }); put(BuildVisibility.class, (type, data) -> field(BuildVisibility.class, data)); put(Schematic.class, (type, data) -> { Object result = fieldOpt(Loadouts.class, data); diff --git a/core/src/mindustry/mod/DataPatcher.java b/core/src/mindustry/mod/DataPatcher.java index 9cbaddd1fc..8b0a005f08 100644 --- a/core/src/mindustry/mod/DataPatcher.java +++ b/core/src/mindustry/mod/DataPatcher.java @@ -77,6 +77,13 @@ public class DataPatcher{ contentLoader = Vars.content.copy(); patches.clear(); + Attribute[] oldAttributes = Attribute.all.clone(); + var oldAttributeMap = Attribute.map.copy(); + reset(() -> { + Attribute.all = oldAttributes; + Attribute.map = oldAttributeMap; + }); + for(String patch : patchArray){ PatchSet set = new PatchSet(patch, new JsonValue("error")); patches.add(set); diff --git a/gradle.properties b/gradle.properties index 405687d605..60ae5cad24 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,4 +26,4 @@ org.gradle.caching=true org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 android.enableR8.fullMode=false -archash=d670ef7b3d +archash=3e5d2eb442 diff --git a/tests/src/test/java/PatcherTests.java b/tests/src/test/java/PatcherTests.java index b68c795f72..95cbf15e42 100644 --- a/tests/src/test/java/PatcherTests.java +++ b/tests/src/test/java/PatcherTests.java @@ -524,6 +524,26 @@ public class PatcherTests{ assertEquals(oldDamage, UnitTypes.dagger.weapons.first().bullet.damage); } + @Test + void customAttribute() throws Exception{ + int amount = Attribute.all.length; + + Vars.state.patcher.apply(Seq.with(""" + block.grass.attributes: { + frogs: 10 + } + """)); + + assertTrue(Attribute.exists("frogs")); + assertEquals(amount + 1, Attribute.all.length); + assertEquals(10f, Blocks.grass.asFloor().attributes.get(Attribute.get("frogs")), 0.0001f); + + Vars.logic.reset(); + + assertFalse(Attribute.exists("frogs")); + assertEquals(amount, Attribute.all.length); + } + @Test void addWeapon() throws Exception{ int oldSize = UnitTypes.flare.weapons.size;