From 8fca45468eac24a0ecbbc08794ad0154e2eccd6b Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 15 Dec 2025 11:19:47 -0500 Subject: [PATCH] Data patcher unit controller support / Warnings for invalid sounds in JSON/DP --- core/src/mindustry/core/FileTree.java | 18 ++++++++++++++++-- core/src/mindustry/mod/ContentParser.java | 23 ++++++++++++++++++++--- core/src/mindustry/mod/DataPatcher.java | 12 +++++++++++- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/core/src/mindustry/core/FileTree.java b/core/src/mindustry/core/FileTree.java index 06c6c3f96d..68eb530c08 100644 --- a/core/src/mindustry/core/FileTree.java +++ b/core/src/mindustry/core/FileTree.java @@ -7,6 +7,7 @@ import arc.assets.loaders.SoundLoader.*; import arc.audio.*; import arc.files.*; import arc.struct.*; +import arc.util.*; import mindustry.*; import mindustry.gen.*; @@ -57,9 +58,11 @@ public class FileTree implements FileHandleResolver{ return loadedSounds.get(soundName, () -> { String name = "sounds/" + soundName; - String path = Vars.tree.get(name + ".ogg").exists() ? name + ".ogg" : name + ".mp3"; + String path = getAudioPath(name); var sound = new Sound(); + + if(path == null) return sound; var desc = Core.assets.load(path, Sound.class, new SoundParameter(sound)); desc.errored = Throwable::printStackTrace; @@ -76,13 +79,24 @@ public class FileTree implements FileHandleResolver{ return loadedMusic.get(musicName, () -> { String name = "music/" + musicName; - String path = Vars.tree.get(name + ".ogg").exists() ? name + ".ogg" : name + ".mp3"; + String path = getAudioPath(name); var music = new Music(); + + if(path == null) return music; var desc = Core.assets.load(path, Music.class, new MusicParameter(music)); desc.errored = Throwable::printStackTrace; return music; }); } + + private static @Nullable String getAudioPath(String name){ + Fi ogg = Vars.tree.get(name + ".ogg"), mp3 = Vars.tree.get(name + ".mp3"); + if(ogg.exists()) return name + ".ogg"; + if(mp3.exists()) return name + ".mp3"; + + Log.warn("Audio file not found: @ (.mp3 or .ogg)", name); + return null; + } } diff --git a/core/src/mindustry/mod/ContentParser.java b/core/src/mindustry/mod/ContentParser.java index dd93a8a1df..4880f6db9a 100644 --- a/core/src/mindustry/mod/ContentParser.java +++ b/core/src/mindustry/mod/ContentParser.java @@ -28,6 +28,7 @@ import mindustry.entities.effect.*; import mindustry.entities.part.*; import mindustry.entities.part.DrawPart.*; import mindustry.entities.pattern.*; +import mindustry.entities.units.*; import mindustry.game.*; import mindustry.game.Objectives.*; import mindustry.gen.*; @@ -62,6 +63,8 @@ public class ContentParser{ Seq listeners = new Seq<>(); /** If false, arbitrary class names cannot be resolved with Class.forName. */ boolean allowClassResolution = true; + /** If false, sound asset loading is disabled. */ + boolean allowAssetLoading = true; ObjectMap, FieldParser> classParsers = new ObjectMap<>(){{ put(Effect.class, (type, data) -> { @@ -298,11 +301,20 @@ public class ContentParser{ if(data.isArray()) return new RandomSound(parser.readValue(Sound[].class, data)); var field = fieldOpt(Sounds.class, data); + + if(!allowAssetLoading && field == null){ + warn("Sound not found: @", data.asString()); + return Sounds.none; + } return field != null ? field : Vars.tree.loadSound(data.asString()); }); put(Music.class, (type, data) -> { var field = fieldOpt(Musics.class, data); + if(!allowAssetLoading && field == null){ + warn("Music not found: @", data.asString()); + return new Music(); + } return field != null ? field : Vars.tree.loadMusic(data.asString()); }); put(Objectives.Objective.class, (type, data) -> { @@ -602,16 +614,15 @@ public class ContentParser{ }else{ throw new IllegalArgumentException("Missing a valid 'block' in 'requirements'"); } - } if(value.has("controller") || value.has("aiController")){ - unit.aiController = supply(resolve(value.getString("controller", value.getString("aiController", "")), FlyingAI.class)); + unit.aiController = resolveController(value.getString("controller", value.getString("aiController", ""))); value.remove("controller"); } if(value.has("defaultController")){ - var sup = supply(resolve(value.getString("defaultController"), FlyingAI.class)); + var sup = resolveController(value.getString("defaultController")); unit.controller = u -> sup.get(); value.remove("defaultController"); } @@ -1094,6 +1105,12 @@ public class ContentParser{ } } + Prov resolveController(String type){ + //this is used as a captured value to avoid parsing it multiple times + var controller = supply(resolve(type, FlyingAI.class)); + return controller::get; + } + Object field(Class type, JsonValue value){ return field(type, value.asString()); } diff --git a/core/src/mindustry/mod/DataPatcher.java b/core/src/mindustry/mod/DataPatcher.java index 7e811ae136..6900d72f46 100644 --- a/core/src/mindustry/mod/DataPatcher.java +++ b/core/src/mindustry/mod/DataPatcher.java @@ -10,6 +10,8 @@ import mindustry.*; import mindustry.core.*; import mindustry.ctype.*; import mindustry.entities.part.*; +import mindustry.entities.units.*; +import mindustry.gen.*; import mindustry.type.*; import mindustry.world.*; import mindustry.world.blocks.*; @@ -54,6 +56,7 @@ public class DataPatcher{ } }; cont.allowClassResolution = false; + cont.allowAssetLoading = false; return cont; } @@ -351,7 +354,14 @@ public class DataPatcher{ var fields = parser.getJson().getFields(actualType); var fdata = fields.get(field); var fobj = object; - if(fdata != null){ + + if(value instanceof JsonValue jsv && object instanceof UnitType && field.equals("controller")){ + var fmeta = fields.get("controller"); + assignValue(object, "controller", new FieldData(fmeta), () -> Reflect.get(fobj, fmeta.field), val -> Reflect.set(fobj, fmeta.field, val), (Func)(u -> parser.resolveController(jsv.asString()).get()), true); + }else if(value instanceof JsonValue jsv && object instanceof UnitType && field.equals("aiController")){ + var fmeta = fields.get("aiController"); + assignValue(object, "aiController", new FieldData(fmeta), () -> Reflect.get(fobj, fmeta.field), val -> Reflect.set(fobj, fmeta.field, val), parser.resolveController(jsv.asString()), true); + }else if(fdata != null){ if(checkField(fdata.field)) return; assignValue(object, field, new FieldData(fdata), () -> Reflect.get(fobj, fdata.field), fv -> {