From d7baddecdda0c3cc3bf1613618e516ae2bb8f184 Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 6 Aug 2026 11:55:40 -0400 Subject: [PATCH] Fixed drawables being cached after data image reload --- core/src/mindustry/mod/DataImagePacker.java | 5 +- core/src/mindustry/ui/builder/UiBuilder.java | 5 + .../src/mindustry/ui/builder/UiDslWriter.java | 127 ++++++++++++++++++ gradle.properties | 2 +- 4 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 core/src/mindustry/ui/builder/UiDslWriter.java diff --git a/core/src/mindustry/mod/DataImagePacker.java b/core/src/mindustry/mod/DataImagePacker.java index 713e1ae223..c6f7ad438b 100644 --- a/core/src/mindustry/mod/DataImagePacker.java +++ b/core/src/mindustry/mod/DataImagePacker.java @@ -175,7 +175,10 @@ public class DataImagePacker{ if(texture != null) patchAtlas.getTextures().remove(texture); } for(var region : patchAtlas.getRegions()){ - if(region != null) patchAtlas.getRegionMap().remove(region.name); + if(region != null){ + patchAtlas.getRegionMap().remove(region.name); + patchAtlas.getDrawables().remove(region.name); + } } patchAtlas.dispose(); diff --git a/core/src/mindustry/ui/builder/UiBuilder.java b/core/src/mindustry/ui/builder/UiBuilder.java index ccb37606f0..b07aaa2493 100644 --- a/core/src/mindustry/ui/builder/UiBuilder.java +++ b/core/src/mindustry/ui/builder/UiBuilder.java @@ -172,6 +172,11 @@ public class UiBuilder{ for(Entry e : entries) if(e.key == key && e.value instanceof Boolean b) return b; return def; } + + @Override + public String toString(){ + return UiDslWriter.write(this); + } } static class Entry{ diff --git a/core/src/mindustry/ui/builder/UiDslWriter.java b/core/src/mindustry/ui/builder/UiDslWriter.java new file mode 100644 index 0000000000..5ce4b90eb4 --- /dev/null +++ b/core/src/mindustry/ui/builder/UiDslWriter.java @@ -0,0 +1,127 @@ +package mindustry.ui.builder; + +import arc.struct.*; +import arc.util.*; +import mindustry.ui.builder.UiBuilder.*; + +/** Converts a parsed NodeBuilder tree back into UI DSL source text. */ +public class UiDslWriter{ + + /** Writes the root node's entries (no wrapping block, since root is an implicit table body). */ + public static String write(NodeBuilder root){ + StringBuilder sb = new StringBuilder(); + writeEntries(sb, root.entries, 0); + return sb.toString(); + } + + private static void writeEntries(StringBuilder sb, Seq entries, int depth){ + for(Entry e : entries){ + // "row" marker + if(e.key == UiKey.row && e.value instanceof Boolean){ + indent(sb, depth); + sb.append("row\n"); + continue; + } + + // nested node (child added via block, shorthand, or bare) + if(e.value instanceof NodeBuilder child){ + writeChildNode(sb, child, depth); + continue; + } + + // plain property: key: value + indent(sb, depth); + sb.append(e.key.name()).append(": ").append(formatValue(e.value)).append('\n'); + } + } + + private static void writeChildNode(StringBuilder sb, NodeBuilder child, int depth){ + indent(sb, depth); + sb.append(child.type.name()); + + UiKey shorthandKey = shorthandType(child.type); + Entry shorthand = null; + for(Entry e : child.entries){ + if(e.key == shorthandKey && e.value instanceof String){ + shorthand = e; + break; + } + } + + Seq rest = new Seq<>(); + for(Entry e : child.entries){ + if(e != shorthand) rest.add(e); + } + + if(shorthand != null){ + sb.append(": ").append(formatValue(shorthand.value)); + } + + if(rest.isEmpty()){ + sb.append('\n'); + }else{ + if(shorthand != null) sb.append(' '); + sb.append("{\n"); + writeEntries(sb, rest, depth + 1); + indent(sb, depth); + sb.append("}\n"); + } + } + + /** Mirrors UiDslParser's getShorthandType: image uses region, everything else uses text. */ + private static UiKey shorthandType(UiKey key){ + return switch(key){ + case image -> UiKey.region; + default -> UiKey.text; + }; + } + + private static String formatValue(Object value){ + if(value instanceof Boolean b) return b ? "true" : "false"; + if(value instanceof Float f) return formatFloat(f); + if(value instanceof String s) return needsQuotes(s) ? quote(s) : s; + throw new IllegalArgumentException("Unsupported value type: " + value); + } + + private static String formatFloat(float f){ + if(f == (long)f) return Long.toString((long)f); + return Float.toString(f); + } + + /** A bare token round-trips correctly only if the parser wouldn't reinterpret it as a bool/number/keyword. */ + private static boolean needsQuotes(String s){ + if(s.isEmpty()) return true; + if(s.equals("true") || s.equals("false")) return true; + if(!Float.isNaN(Strings.parseFloat(s, Float.NaN))) return true; + + for(int i = 0; i < s.length(); i++){ + char c = s.charAt(i); + if(Character.isWhitespace(c) || c == '{' || c == '}' || c == ':' || c == '"' || c == '\\'){ + return true; + } + } + // avoid accidentally starting a line comment + return s.startsWith("//"); + } + + private static String quote(String s){ + StringBuilder sb = new StringBuilder(); + sb.append('"'); + for(int i = 0; i < s.length(); i++){ + char c = s.charAt(i); + if(c == '"' || c == '\\'){ + sb.append('\\').append(c); + }else if(c == '\n'){ + sb.append("\\n"); + }else{ + sb.append(c); + } + } + sb.append('"'); + return sb.toString(); + } + + private static void indent(StringBuilder sb, int depth){ + for(int i = 0; i < depth; i++) sb.append(" "); + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 898f75af13..638dcb2fdf 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=bd04d32df8 +archash=33cbf6381b