Jval migration made functional

This commit is contained in:
Anuken
2026-08-01 22:53:46 -04:00
parent cb6c5b1c5d
commit 8fad301da8
14 changed files with 83 additions and 82 deletions
@@ -40,8 +40,6 @@ public class EntityIO{
this.serializer = serializer;
this.name = name;
json.setIgnoreUnknownFields(true);
directory.mkdirs();
//load old revisions
+2 -3
View File
@@ -10,7 +10,6 @@ import arc.util.*;
import arc.util.CommandHandler.*;
import arc.util.io.*;
import arc.util.serialization.*;
import arc.util.serialization.JsonValue.*;
import mindustry.*;
import mindustry.annotations.Annotations.*;
import mindustry.audio.*;
@@ -434,8 +433,8 @@ public class NetClient implements ApplicationListener{
public static void setRule(String rule, String jsonData){
try{
//readField searches for the specified value, so create a fake parent for it.
tmpJsonMap.child = null;
tmpJsonMap.addChild(rule, new JsonReader().parse(jsonData));
tmpJsonMap.clear().put(rule, Jval.read(jsonData));
JsonIO.json.readField(state.rules, rule, tmpJsonMap);
}catch(Throwable error){
Log.err("Failed to read rule", error);
@@ -150,8 +150,14 @@ public class MapPatchesView implements AssetView{
int countFields(Jval value){
if(value.isObject() || value.isArray()){
int sum = 0;
for(var child : value){
sum += countFields(child);
if(value.isObject()){
for(var child : value.asObject()){
sum += countFields(child.value);
}
}else{
for(var child : value.asArray()){
sum += countFields(child);
}
}
return Math.max(sum, 1);
}else{
+2 -2
View File
@@ -427,8 +427,8 @@ public class Rules{
@Override
public void read(Json json, Jval jsonData){
for(Jval value : jsonData){
values[Integer.parseInt(value.name)] = json.readValue(TeamRule.class, value);
for(var entry : jsonData.asObject()){
values[Integer.parseInt(entry.key)] = json.readValue(TeamRule.class, entry.value);
}
}
}
+8 -7
View File
@@ -5,6 +5,7 @@ import arc.math.geom.*;
import arc.util.*;
import arc.util.serialization.*;
import arc.util.serialization.Json.*;
import arc.util.serialization.Jval.*;
import mindustry.*;
import mindustry.audio.*;
import mindustry.content.*;
@@ -25,7 +26,7 @@ public class JsonIO{
public void writeValue(Object value, Class knownType, Class elementType){
if(value instanceof MappableContent c){
try{
getWriter().value(c.name);
writer.value(c.name);
}catch(IOException e){
throw new RuntimeException(e);
}
@@ -56,7 +57,7 @@ public class JsonIO{
}
public static <T> T readBytes(Class<T> type, Class<?> elementType, DataInputStream input) throws IOException{
return json.readValue(type, elementType, new UBJsonReader().parseWihoutClosing(input));
return json.readValue(type, elementType, UBJson.read(input));
}
public static String write(Object object){
@@ -77,12 +78,12 @@ public class JsonIO{
}
public static <T> T read(T base, String string){
json.readFields(base, new JsonReader().parse(string.replace("io.anuke.", "")));
json.readFields(base, Jval.read(string.replace("io.anuke.", "")));
return base;
}
public static String print(String in){
return json.prettyPrint(in);
return Jval.read(in).toString(Jformat.hjson);
}
public static void classTag(String tag, Class<?> type){
@@ -312,7 +313,7 @@ public class JsonIO{
public MapObjectives read(Json json, Jval data, Class type){
var exec = new MapObjectives();
// First iteration to instantiate the objectives.
for(var value = data.child; value != null; value = value.next){
for(var value : data.asArray()){
//glenn why did you implement this in the least backwards compatible way possible
//the old objectives had lowercase class tags, now they're uppercase and either way I can't deserialize them without errors
if(value.has("class") && Character.isLowerCase(value.getString("class").charAt(0))){
@@ -333,8 +334,8 @@ public class JsonIO{
// Second iteration to map the parents.
int i = 0;
for(var value = data.child; value != null; value = value.next, i++){
for(var parent = value.get("parents").child; parent != null; parent = parent.next){
for(var entry : data.asArray()){
for(var parent : entry.get("parents").asArray()){
int val = parent.asInt();
if(val >= 0 && val < exec.all.size){
exec.all.get(i).parents.add(exec.all.get(val));
+1 -1
View File
@@ -67,7 +67,7 @@ public class LogicScript implements JsonSerializable{
public void read(Json json, Jval jsonData){
if(jsonData.isObject()){
timeout = Math.min(maxTimeoutMs, jsonData.getInt("timeout", 0));
resetVars = jsonData.getBoolean("resetVars", false);
resetVars = jsonData.getBool("resetVars", false);
script = jsonData.getString("script", "");
}else{
script = jsonData.asString();
+1 -1
View File
@@ -366,7 +366,7 @@ public class Maps{
if(groups == null) return "[]";
StringWriter buffer = new StringWriter();
JsonIO.json.setWriter(new JsonWriter(buffer));
JsonIO.json.setWriter(new StringJsonWriter(buffer));
JsonIO.json.writeArrayStart();
for(int i = 0; i < groups.size; i++){
+43 -47
View File
@@ -96,9 +96,9 @@ public class ContentParser{
throw new IllegalArgumentException("Attribute definitions must be objects, e.g. {heat: 10}");
}
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());
for(var entry : data.asObject()){
Attribute value = Attribute.exists(entry.key) ? Attribute.get(entry.key) : Attribute.add(entry.key);
attr.set(value, entry.value.asFloat());
}
return attr;
});
@@ -127,7 +127,7 @@ public class ContentParser{
}
}
TextureRegion result = Core.atlas.find(str);
if(!result.found()){
if(!result.found() && !str.equalsIgnoreCase("error")){
warn("Sprite not found: '" + str + "'");
}
return result;
@@ -242,21 +242,18 @@ public class ContentParser{
//no singular operation, check for multi-operation
if(opval == null){
Jval opsVal =
data.has("operations") ? data.get("operations") :
data.has("ops") ? data.get("ops") : null;
var opsVal =
data.has("operations") ? data.get("operations").asArray() :
data.has("ops") ? data.get("ops").asArray() : null;
if(opsVal != null){
if(!opsVal.isArray()) throw new RuntimeException("Chained PartProgress operations must be an array.");
int i = 0;
while(true){
for(int i = 0; i < opsVal.size; i ++){
Jval val = opsVal.get(i);
if(val == null) break;
Jval op = val.has("operation") ? val.get("operation") :
val.has("op") ? val.get("op") : null;
base = parseProgressOp(base, op.asString(), val);
i++;
}
}
@@ -284,21 +281,21 @@ public class ContentParser{
}
//transform array format
if(data.isArray() && data.size == 3){
if(data.isArray() && data.asArray().size == 3){
return new Mat3D().setToTranslation(new Vec3(data.asFloatArray()));
}
Mat3D mat = new Mat3D();
//TODO this is kinda bad
for(var val : data){
switch(val.name){
for(var entry : data.asObject()){
switch(entry.key){
case "translate", "trans" -> mat.translate(parser.readValue(Vec3.class, data));
case "scale", "scl" -> mat.scale(parser.readValue(Vec3.class, data));
case "rotate", "rot" -> mat.rotate(parser.readValue(Vec3.class, data), data.getFloat("degrees", 0f));
case "multiply", "mul" -> mat.mul(parser.readValue(Mat3D.class, data));
case "x", "y", "z" -> {}
default -> throw new RuntimeException("Unknown matrix transformation: '" + val.name + "'");
default -> throw new RuntimeException("Unknown matrix transformation: '" + entry.key + "'");
}
}
@@ -454,7 +451,7 @@ public class ContentParser{
//try to parse env bits
if((type == int.class || type == Integer.class) && jsonData.isArray()){
int value = 0;
for(var str : jsonData){
for(var str : jsonData.asArray()){
if(!str.isString()) throw new SerializationException("Integer bitfield values must all be strings. Found: " + str);
String field = str.asString();
value |= Reflect.<Integer>get(Env.class, field);
@@ -467,7 +464,7 @@ public class ContentParser{
if(type == ItemStack.class && jsonData.isString() && jsonData.asString().contains("/")){
String[] split = jsonData.asString().split("/");
return (T)fromJson(ItemStack.class, "{item: " + split[0] + ", amount: " + split[1] + "}");
return (T)new ItemStack(fromJson(Item.class, split[0]), Integer.parseInt(split[1]));
}
//try to parse "payloaditem/amount" syntax
@@ -490,8 +487,9 @@ public class ContentParser{
}
//try to parse Rect as array
if(type == Rect.class && jsonData.isArray() && jsonData.size == 4){
return (T)new Rect(jsonData.get(0).asFloat(), jsonData.get(1).asFloat(), jsonData.get(2).asFloat(), jsonData.get(3).asFloat());
if(type == Rect.class && jsonData.isArray() && jsonData.asArray().size == 4){
JsonArray arr = jsonData.asArray();
return (T)new Rect(arr.get(0).asFloat(), arr.get(1).asFloat(), arr.get(2).asFloat(), arr.get(3).asFloat());
}
//search across different content types to find one by name
@@ -502,7 +500,7 @@ public class ContentParser{
return found;
}
}
throw new IllegalArgumentException("\"" + jsonData.name + "\": No content found with name '" + jsonData.asString() + "'.");
throw new IllegalArgumentException("No content found with name '" + jsonData.asString() + "'.");
}
if(Content.class.isAssignableFrom(type)){
@@ -513,7 +511,7 @@ public class ContentParser{
T two = (T)Vars.content.getByName(ctype, jsonData.asString());
if(two != null) return two;
throw new IllegalArgumentException((jsonData.name == null ? "" : "\"" + jsonData.name + "\": ") + "No " + ctype + " found with name '" + jsonData.asString() + "'.\nMake sure '" + jsonData.asString() + "' is spelled correctly, and that it really exists!\nThis may also occur because its file failed to parse.");
throw new IllegalArgumentException("No " + ctype + " found with name '" + jsonData.asString() + "'.\nMake sure '" + jsonData.asString() + "' is spelled correctly, and that it really exists!\nThis may also occur because its file failed to parse.");
}
}
@@ -522,8 +520,11 @@ public class ContentParser{
};
public void readBlockConsumers(Block block, Jval value){
for(Jval child : value){
switch(child.name){
for(var entry : value.asObject()){
String name = entry.key;
Jval child = entry.value;
switch(name){
case "remove" -> {
String[] values = child.isString() ? new String[]{child.asString()} : child.asStringArray();
for(String type : values){
@@ -569,7 +570,7 @@ public class ContentParser{
}
}
case "powerBuffered" -> block.consumePowerBuffered(child.asFloat());
default -> throw new IllegalArgumentException("Unknown consumption type: '" + child.name + "' for block '" + block.name + "'.");
default -> throw new IllegalArgumentException("Unknown consumption type: '" + name + "' for block '" + block.name + "'.");
}
}
value.remove("consumes");
@@ -1024,15 +1025,10 @@ public class ContentParser{
public Content parse(LoadedMod mod, String name, String json, Fi file, ContentType type) throws Exception{
checkInit();
//remove extra # characters to make it valid json... apparently some people have *unquoted* # characters in their json
if(file.extension().equals("json")){
json = json.replace("#", "\\#");
}
currentFile = file;
currentMod = mod;
var rawValue = parser.fromJson(null, Jval.read(json).toString(Jformat.plain));
var rawValue = parser.fromJson(null, json);
if(!(rawValue instanceof Jval value)) throw new SerializationException("Content JSON must be an object, not a single value.");
if(!parsers.containsKey(type)){
@@ -1113,12 +1109,7 @@ public class ContentParser{
}
private GenericMesh[] parseMeshes(Planet planet, Jval array){
var res = new GenericMesh[array.size];
for(int i = 0; i < array.size; i++){
//yes get is O(n) but it's practically irrelevant here
res[i] = parseMesh(planet, array.get(i));
}
return res;
return array.asArray().map(value -> parseMesh(planet, value)).toArray(GenericMesh.class);
}
private GenericMesh parseMesh(Planet planet, Jval data){
@@ -1274,15 +1265,18 @@ public class ContentParser{
toBeParsed.remove(object);
var type = object.getClass();
var fields = parser.getFields(type);
for(Jval child = jsonMap.child; child != null; child = child.next){
FieldMetadata metadata = fields.get(child.name().replace(" ", "_"));
for(var entry : jsonMap.asObject()){
String name = entry.key;
Jval child = entry.value;
FieldMetadata metadata = fields.get(name.replace(" ", "_"));
if(metadata == null){
if(ignoreUnknownFields){
warn("@Unknown field '@' for class '@'", currentContent == null ? "" : "[" + currentContent.minfo.sourceFile.name() + "]: ", child.name, type.getSimpleName());
warn("@Unknown field '@' for class '@'", currentContent == null ? "" : "[" + currentContent.minfo.sourceFile.name() + "]: ", name, type.getSimpleName());
continue;
}else{
SerializationException ex = new SerializationException("Field not found: " + child.name + " (" + type.getName() + ")");
ex.addTrace(child.trace());
SerializationException ex = new SerializationException("Field not found: " + name + " (" + type.getName() + ")");
ex.addTrace(child.toString());
throw ex;
}
}
@@ -1301,7 +1295,7 @@ public class ContentParser{
}
}else{
boolean isMap = ObjectMap.class.isAssignableFrom(field.getType()) || ObjectIntMap.class.isAssignableFrom(field.getType()) || ObjectFloatMap.class.isAssignableFrom(field.getType());
boolean mergeMap = isMap && child.has("add") && child.get("add").isBoolean() && child.getBoolean("add", false);
boolean mergeMap = isMap && child.has("add") && child.get("add").isBoolean() && child.getBool("add", false);
if(mergeMap){
child.remove("add");
@@ -1330,7 +1324,7 @@ public class ContentParser{
throw ex;
}catch(RuntimeException runtimeEx){
SerializationException ex = new SerializationException(runtimeEx);
ex.addTrace(child.trace());
ex.addTrace(child.toString());
ex.addTrace(field.getName() + " (" + type.getName() + ")");
throw ex;
}
@@ -1370,8 +1364,10 @@ public class ContentParser{
currentMod = cur;
currentFile = file;
boolean isObject = research.isObject();
//add custom objectives
if(research.has("objectives")){
if(isObject && research.has("objectives")){
node.objectives.addAll(parser.readValue(Objective[].class, research.get("objectives")));
}
@@ -1389,13 +1385,13 @@ public class ContentParser{
node.setupRequirements(unlock.researchRequirements());
}
if(research.has("planet")){
if(isObject && research.has("planet")){
node.planet = find(ContentType.planet, research.getString("planet"));
}
if(research.getBoolean("root", false)){
if(isObject && research.getBool("root", false)){
node.name = research.getString("name", unlock.name);
node.requiresUnlock = research.getBoolean("requiresUnlock", false);
node.requiresUnlock = research.getBool("requiresUnlock", false);
TechTree.roots.add(node);
}else{
if(researchName != null){
+9 -9
View File
@@ -249,8 +249,8 @@ public class DataPatcher{
set.name = value.getString("name", "");
value.remove("name"); //patchsets can have a name, ignore it if present
for(var child : value){
assign(root, child.name, child, null, null, null);
for(var entry : value.asObject()){
assign(root, entry.key, entry.value, null, null, null);
}
currentlyApplyingPatch = null;
@@ -413,8 +413,8 @@ public class DataPatcher{
if(object == root){
if(value instanceof Jval jval && jval.isObject()){
for(var child : jval){
assign(root, field + "." + child.name, child, null, null, null);
for(var entry : jval.asObject()){
assign(root, field + "." + entry.key, entry.value, null, null, null);
}
}else{
warn("Content '@' cannot be assigned.", field);
@@ -661,14 +661,14 @@ public class DataPatcher{
//assign each field manually
var childFields = parser.getJson().getFields(prevValue.getClass().isAnonymousClass() ? prevValue.getClass().getSuperclass() : prevValue.getClass());
for(var child : jsv){
if(child.name != null){
assign(prevValue, child.name, child,
for(var entry : jsv.asObject()){
if(entry.key != null){
assign(prevValue, entry.key, entry.value,
metadata != null && (metadata.type == ObjectMap.class || metadata.type == ObjectFloatMap.class) ? metadata :
metadata != null && metadata.type == Seq.class ? new FieldData(metadata.elementType, null, null) :
metadata != null && metadata.type.isArray() ? new FieldData(metadata.type.getComponentType(), null, null) :
!childFields.containsKey(child.name) ? null :
new FieldData(childFields.get(child.name)), object, field);
!childFields.containsKey(entry.key) ? null :
new FieldData(childFields.get(entry.key)), object, field);
}
}
}
+1 -1
View File
@@ -69,6 +69,6 @@ public class PatchAsset extends DataAsset{
@Override
public String toString(){
//the json can be a single 'error' value if it failed to parse
return !json.isObject() ? patch : json.prettyPrint(OutputType.minimal, 2);
return !json.isObject() ? patch : json.toString(Jformat.hjson);
}
}
+4 -4
View File
@@ -20,14 +20,14 @@ public class MapLocales extends ObjectMap<String, StringMap> implements JsonSeri
@Override
public void read(Json json, Jval jsonData){
for(Jval value : jsonData){
for(var entry : jsonData.asObject()){
StringMap map = new StringMap();
for(Jval child = value.child; child != null; child = child.next){
map.put(child.name, json.readValue(String.class, null, child));
for(var innerEntry : entry.value.asObject()){
map.put(innerEntry.key, innerEntry.value.asString());
}
put(value.name, map);
put(entry.key, map);
}
}
@@ -256,7 +256,7 @@ public class CustomRulesDialog extends BaseDialog{
Boolp allowMusic = () -> !rules.disableMusic;
Func<String, Seq<MusicContainer>> parser = str -> {
try{
return Seq.map(new JsonReader().parse("[" + str + "]").asStringArray(), MusicContainer::new);
return Jval.read("[" + str + "]").asArray().map( j -> new MusicContainer(j.asString()));
}catch(Throwable e){
return null;
}
+1 -1
View File
@@ -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=c2632c8f1b
archash=36256993a0
+2 -1
View File
@@ -6,6 +6,7 @@ import arc.struct.*;
import arc.util.*;
import arc.util.io.*;
import arc.util.serialization.*;
import arc.util.serialization.Jval.*;
import mindustry.*;
import mindustry.content.*;
import mindustry.core.*;
@@ -231,7 +232,7 @@ public class ApplicationTests{
for(String file : files){
try{
String str = Core.files.absolute("./../../" + file).readString();
assertEquals(ValueType.array, new JsonReader().parse(str).type());
assertEquals(Jtype.array, Jval.read(str).getType());
assertTrue(Jval.read(str).isArray());
JSONArray array = new JSONArray(str);
assertTrue(array.length() > 0);