Skip to content

Commit

Permalink
Add Global Mod Aliases config file
Browse files Browse the repository at this point in the history
Relates to #399
  • Loading branch information
Su5eD committed Oct 22, 2023
1 parent d321d45 commit a0e055d
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 4 deletions.
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,35 @@ Here's a few tips to help get your PR approved:
* A PR should be focused on content, rather than syntax changes.
* Use the file you are editing as a style guide.
* Make sure your feature isn't already in the works, or hasn't been rejected previously.

## Configuration

### Global Mod Aliases

To improve mod compatibility, Connector provides a Global Mod Alias feature that can be used to provide alternative IDs
for mods in the fabric loader. Similar to fabric's [Dependency Overrides](https://fabricmc.net/wiki/tutorial:dependency_overrides),
it uses a json config file to define aliases.

Global Mod Aliases are defined in a file named `connector_global_mod_aliases.json`, located inside your config folder.
If it doesn't exist yet, Connector will create a new one with its default mod aliases.

Here's a minimal configuration example:
```json
{
"version": 1,
"aliases": {
"cloth_config": "cloth-config2",
"embeddium": [
"sodium",
"rubidium"
]
}
}
```

Let's go over it line-by-line.
- First, we have `version`, which specifies the config file spec version we would like to use.
At the time of writing, the latest version is version 1.
- Secondly, we have `aliases`. This JSON object contains all of our alises for various mods.
Keys inside the object represent mod IDs to be aliased. The value can be either a single **string**, or an **array** in case
we want to provide multiple aliases for one mod.
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ org.gradle.daemon=true
# Versions
versionConnector=1.0.0-beta.20
versionAdapter=1.7.0-1.20.1-20231012.193807
versionAdapterDefinition=1.8.0
versionAdapterDefinition=1.8.1

versionMc=1.20.1
versionForge=47.1.3
versionForgeAutoRenamingTool=1.0.8
versionFabricLoader=2.5.1+0.14.21+1.20.1
versionAccessWidener=2.1.0
versionFabricApi=0.90.0+1.9.26+1.20.1
versionFabricApi=0.90.0+1.9.27+1.20.1
versionMixin=0.12.5+mixin.0.8.5
versionMixinTransmog=0.4.0+1.20.1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public final class ConnectorUtil {
);
// Common aliased mod dependencies that don't work with forge ports, which use a different modid.
// They're too annoying to override individually in each mod, so we provide this small QoL feature for the user's comfort
public static final Multimap<String, String> GLOBAL_MOD_ALIASES = ImmutableMultimap.of(
public static final Multimap<String, String> DEFAULT_GLOBAL_MOD_ALIASES = ImmutableMultimap.of(
"cloth_config", "cloth-config2"
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ public final class DependencyResolver {
private static final Logger LOGGER = LogUtils.getLogger();
public static final VersionOverrides VERSION_OVERRIDES = new VersionOverrides();
public static final DependencyOverrides DEPENDENCY_OVERRIDES = new DependencyOverrides(FMLPaths.CONFIGDIR.get());
private static final GlobalModAliases GLOBAL_MOD_ALIASES = new GlobalModAliases(FMLPaths.CONFIGDIR.get(), ConnectorUtil.DEFAULT_GLOBAL_MOD_ALIASES);

public static List<JarTransformer.TransformableJar> resolveDependencies(Collection<JarTransformer.TransformableJar> keys, Multimap<JarTransformer.TransformableJar, JarTransformer.TransformableJar> jars, Iterable<IModFile> loadedMods) {
// Add global mod aliases
FabricLoaderImpl.INSTANCE.aliasMods(ConnectorUtil.GLOBAL_MOD_ALIASES);
FabricLoaderImpl.INSTANCE.aliasMods(GLOBAL_MOD_ALIASES.getAliases());
BiMap<JarTransformer.TransformableJar, ModCandidate> jarToCandidate = HashBiMap.create();
// Fabric candidates
List<ModCandidate> candidates = createCandidatesRecursive(keys, keys, jars, jarToCandidate);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package dev.su5ed.sinytra.connector.locator;

import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.stream.JsonWriter;
import com.mojang.logging.LogUtils;
import net.fabricmc.loader.impl.FormattedException;
import net.fabricmc.loader.impl.lib.gson.JsonReader;
import net.fabricmc.loader.impl.lib.gson.JsonToken;
import net.fabricmc.loader.impl.metadata.ParseMetadataException;
import net.fabricmc.loader.impl.util.LoaderUtil;
import org.slf4j.Logger;

import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Map;

public class GlobalModAliases {
private static final Logger LOGGER = LogUtils.getLogger();

private final Multimap<String, String> aliases;

public GlobalModAliases(Path configDir, Multimap<String, String> defaultValues) {
Path path = configDir.resolve("connector_global_mod_aliases.json");

if (!Files.exists(path)) {
this.aliases = ImmutableMultimap.copyOf(defaultValues);
write(path);
return;
}

try (JsonReader reader = new JsonReader(new InputStreamReader(Files.newInputStream(path), StandardCharsets.UTF_8))) {
this.aliases = parse(reader);
} catch (IOException | ParseMetadataException e) {
throw FormattedException.ofLocalized("exception.parsingOverride", "Failed to parse " + LoaderUtil.normalizePath(path), e);
}
}

public Multimap<String, String> getAliases() {
return this.aliases;
}

private Multimap<String, String> parse(JsonReader reader) throws IOException, ParseMetadataException {
if (reader.peek() != JsonToken.BEGIN_OBJECT) {
throw new ParseMetadataException("Root must be an object", reader);
}

reader.beginObject();

if (!reader.nextName().equals("version")) {
throw new ParseMetadataException("First key must be \"version\"", reader);
}

if (reader.peek() != JsonToken.NUMBER || reader.nextInt() != 1) {
throw new ParseMetadataException("Unsupported \"version\", must be 1", reader);
}

ImmutableMultimap.Builder<String, String> aliases = ImmutableMultimap.builder();

while (reader.hasNext()) {
String key = reader.nextName();

if ("aliases".equals(key)) {
reader.beginObject();

while (reader.hasNext()) {
String modid = reader.nextName();

switch (reader.peek()) {
case STRING -> {
String alias = reader.nextString();
aliases.put(modid, alias);
}
case BEGIN_ARRAY -> {
reader.beginArray();
while (reader.hasNext()) {
String alias = reader.nextString();
aliases.put(modid, alias);
}
reader.endArray();
}
default -> throw new ParseMetadataException("Mod aliases must be a string or string array!", reader);
}
}

reader.endObject();
}
else {
throw new ParseMetadataException("Unsupported root key: " + key, reader);
}
}

reader.endObject();

return aliases.build();
}

private void write(Path path) {
Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
try {
Files.createDirectories(path.getParent());

try (JsonWriter writer = gson.newJsonWriter(Files.newBufferedWriter(path))) {
writer.beginObject();

writer.name("version").value(1);

writer.name("aliases").beginObject();
for (Map.Entry<String, Collection<String>> entry : this.aliases.asMap().entrySet()) {
Collection<String> values = entry.getValue();
writer.name(entry.getKey());
if (values.size() == 1) {
writer.value(values.iterator().next());
}
else {
writer.beginArray();
for (String value : values) {
writer.value(value);
}
writer.endArray();
}
}
writer.endObject();

writer.endObject();
}
} catch (IOException e) {
LOGGER.error("Error writing default global mod aliases", e);
}
}
}

0 comments on commit a0e055d

Please sign in to comment.