Add DFU for codecs, support NeoForge run config templates

This commit is contained in:
Juuz
2023-11-01 00:44:02 +02:00
parent 0b87379c7e
commit 29fbc7d736
5 changed files with 74 additions and 37 deletions

View File

@@ -81,6 +81,7 @@ repositories {
excludeGroupByRegex "org\\.eclipse\\.?.*"
}
}
maven { url "https://libraries.minecraft.net/" }
}
configurations {
@@ -186,6 +187,7 @@ dependencies {
implementation libs.mcinjector
implementation libs.opencsv
implementation libs.forge.diffpatch
implementation 'com.mojang:datafixerupper:6.0.8'
// Forge mods.toml parsing
implementation libs.night.config.toml

View File

@@ -24,6 +24,8 @@
package net.fabricmc.loom.configuration.providers.forge;
import com.mojang.serialization.Codec;
/**
* A string or a variable in a Forge configuration file, or an MCPConfig step or function.
*/
@@ -41,6 +43,16 @@ public sealed interface ConfigValue {
*/
String LOG = "log";
Codec<ConfigValue> CODEC = Codec.STRING.xmap(ConfigValue::of, configValue -> {
if (configValue instanceof Constant constant) {
return constant.value();
} else if (configValue instanceof Variable variable) {
return "{" + variable.name() + "}";
}
throw new IllegalArgumentException("Unmatched config value");
});
String resolve(Resolver variableResolver);
static ConfigValue of(String str) {

View File

@@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2022 FabricMC
* Copyright (c) 2022-2023 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,18 +24,17 @@
package net.fabricmc.loom.configuration.providers.forge;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import org.gradle.api.Named;
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.Pair;
import net.fabricmc.loom.util.function.CollectionUtil;
public record ForgeRunTemplate(
@@ -46,6 +45,51 @@ public record ForgeRunTemplate(
Map<String, ConfigValue> env,
Map<String, ConfigValue> props
) implements Named {
public static final Codec<ForgeRunTemplate> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.STRING.optionalFieldOf("name", "") // note: empty is used since DFU crashes with null
.forGetter(ForgeRunTemplate::name),
Codec.STRING.fieldOf("main")
.forGetter(ForgeRunTemplate::main),
ConfigValue.CODEC.listOf().optionalFieldOf("args", List.of())
.forGetter(ForgeRunTemplate::args),
ConfigValue.CODEC.listOf().optionalFieldOf("jvmArgs", List.of())
.forGetter(ForgeRunTemplate::jvmArgs),
Codec.unboundedMap(Codec.STRING, ConfigValue.CODEC).optionalFieldOf("env", Map.of())
.forGetter(ForgeRunTemplate::env),
Codec.unboundedMap(Codec.STRING, ConfigValue.CODEC).optionalFieldOf("props", Map.of())
.forGetter(ForgeRunTemplate::props)
).apply(instance, ForgeRunTemplate::new));
public static final Codec<Map<String, ForgeRunTemplate>> MAP_CODEC = Codec.unboundedMap(Codec.STRING, CODEC)
.xmap(
map -> {
final Map<String, ForgeRunTemplate> newMap = new HashMap<>(map);
// Iterate through all templates and fill in empty names.
// The NeoForge format doesn't include the name property, so we'll use the map keys
// as a replacement.
for (Map.Entry<String, ForgeRunTemplate> entry : newMap.entrySet()) {
final ForgeRunTemplate template = entry.getValue();
if (template.name.isEmpty()) {
final ForgeRunTemplate completed = new ForgeRunTemplate(
entry.getKey(),
template.main,
template.args,
template.jvmArgs,
template.env,
template.props
);
entry.setValue(completed);
}
}
return newMap;
},
Function.identity()
);
@Override
public String getName() {
return name;
@@ -66,29 +110,4 @@ public record ForgeRunTemplate(
// Add MOD_CLASSES, this is something that ForgeGradle does
settings.getEnvironmentVariables().computeIfAbsent("MOD_CLASSES", $ -> ConfigValue.of("{source_roots}").resolve(configValueResolver));
}
public static ForgeRunTemplate fromJson(JsonObject json) {
if (json.has("parents") && !json.getAsJsonArray("parents").isEmpty()) {
throw new IllegalArgumentException("Non-empty parents for run config template not supported!");
}
String name = json.getAsJsonPrimitive("name").getAsString();
String main = json.getAsJsonPrimitive("main").getAsString();
List<ConfigValue> args = json.has("args") ? fromJson(json.getAsJsonArray("args")) : List.of();
List<ConfigValue> jvmArgs = json.has("jvmArgs") ? fromJson(json.getAsJsonArray("jvmArgs")) : List.of();
Map<String, ConfigValue> env = json.has("env") ? fromJson(json.getAsJsonObject("env"), ConfigValue::of) : Map.of();
Map<String, ConfigValue> props = json.has("props") ? fromJson(json.getAsJsonObject("props"), ConfigValue::of) : Map.of();
return new ForgeRunTemplate(name, main, args, jvmArgs, env, props);
}
private static List<ConfigValue> fromJson(JsonArray json) {
return CollectionUtil.map(json, child -> ConfigValue.of(child.getAsJsonPrimitive().getAsString()));
}
private static <R> Map<String, R> fromJson(JsonObject json, Function<String, R> converter) {
return json.entrySet().stream().map(entry -> {
String value = entry.getValue().getAsJsonPrimitive().getAsString();
return new Pair<>(entry.getKey(), converter.apply(value));
}).collect(Collectors.toMap(Pair::left, Pair::right));
}
}

View File

@@ -29,7 +29,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -39,6 +39,7 @@ import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.mojang.serialization.JsonOps;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.NamedDomainObjectSet;
import org.gradle.api.Project;
@@ -67,10 +68,11 @@ public class ForgeRunsProvider {
}
private void readTemplates() {
for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject("runs").entrySet()) {
ForgeRunTemplate template = ForgeRunTemplate.fromJson(entry.getValue().getAsJsonObject());
templates.add(template);
}
final JsonObject runJson = json.getAsJsonObject("runs");
final Collection<ForgeRunTemplate> templates = ForgeRunTemplate.MAP_CODEC.parse(JsonOps.INSTANCE, runJson)
.getOrThrow(false, error -> project.getLogger().error("Couldn't read run config templates, {}", error))
.values();
this.templates.addAll(templates);
}
public NamedDomainObjectSet<ForgeRunTemplate> getTemplates() {

View File

@@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2022 FabricMC
* Copyright (c) 2022-2023 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -29,6 +29,7 @@ import java.nio.file.Path
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.mojang.serialization.JsonOps
import spock.lang.Specification
import spock.lang.TempDir
import spock.lang.Unroll
@@ -56,7 +57,8 @@ class ForgeRunTemplateTest extends Specification {
def json = downloadForgeConfig(gameVersion, forgeVersion)
when:
def template = ForgeRunTemplate.fromJson(json.getAsJsonObject("runs").getAsJsonObject("client"))
def result = ForgeRunTemplate.CODEC.parse(JsonOps.INSTANCE, json.getAsJsonObject("runs").getAsJsonObject("client"))
def template = result.getOrThrow(false, {})
then:
template.name == template.name() // check that the name gradle sees matches the name read from the json