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;