From 5192a5258b39f2c69d15f109cfefbd9ecb9591da Mon Sep 17 00:00:00 2001 From: Nerjal Nosk Date: Tue, 20 Oct 2026 12:42:59 +0200 Subject: [PATCH] Now loader-agnostic --- README.md | 15 +- build.gradle | 30 +- gradle.properties | 14 +- settings.gradle | 4 - .../compat/CompatMixinPlugin.java | 29 +- .../moddingutils/extra/SubjectiveOpinion.java | 9 + .../nerjal/moddingutils/mod/ModVersion.java | 28 ++ .../moddingutils/mod/SemanticModVersion.java | 270 ++++++++++++++++++ .../moddingutils/mod/StringModVersion.java | 32 +++ .../mod/VersionParsingException.java | 19 ++ .../nerjal/moddingutils/platform/Loader.java | 40 +++ .../platform/PlatformService.java | 90 ++++++ .../platform/PlatformServiceFabric.java | 27 ++ .../platform/PlatformServiceForge.java | 25 ++ .../platform/PlatformServiceNeoForge.java | 24 ++ .../platform/PlatformServiceOther.java | 13 + .../nerjal/moddingutils/platform/Side.java | 16 ++ src/main/java/module-info.java | 14 + src/main/resources/fabric.mod.json | 35 --- 19 files changed, 658 insertions(+), 76 deletions(-) create mode 100644 src/main/java/com/nerjal/moddingutils/extra/SubjectiveOpinion.java create mode 100644 src/main/java/com/nerjal/moddingutils/mod/ModVersion.java create mode 100644 src/main/java/com/nerjal/moddingutils/mod/SemanticModVersion.java create mode 100644 src/main/java/com/nerjal/moddingutils/mod/StringModVersion.java create mode 100644 src/main/java/com/nerjal/moddingutils/mod/VersionParsingException.java create mode 100644 src/main/java/com/nerjal/moddingutils/platform/Loader.java create mode 100644 src/main/java/com/nerjal/moddingutils/platform/PlatformService.java create mode 100644 src/main/java/com/nerjal/moddingutils/platform/PlatformServiceFabric.java create mode 100644 src/main/java/com/nerjal/moddingutils/platform/PlatformServiceForge.java create mode 100644 src/main/java/com/nerjal/moddingutils/platform/PlatformServiceNeoForge.java create mode 100644 src/main/java/com/nerjal/moddingutils/platform/PlatformServiceOther.java create mode 100644 src/main/java/com/nerjal/moddingutils/platform/Side.java create mode 100644 src/main/java/module-info.java delete mode 100644 src/main/resources/fabric.mod.json diff --git a/README.md b/README.md index 16b6739..80e448c 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ -# Supported platforms - -Fabric (required for platform mod resolution) - -Java >= 17 - # Features +## Platform detection + +Automatic detection of the current modloader (between Fabric, Forge, Neoforge or Other) +and of the current execution side (client or server) using the mixin engine. + +Access these via the `PlatformService` class. + ## Modding compat A mod compatibility mixin plugin is made available at @@ -30,4 +31,4 @@ ModdingUtils. ## Latest version -[1.0](https://maven.nerjal.com/#/releases/com/nerjal/ModdingUtils/1.0) \ No newline at end of file +[1.1](https://maven.nerjal.com/#/releases/com/nerjal/ModdingUtils/1.1) \ No newline at end of file diff --git a/build.gradle b/build.gradle index 5283ad9..f10592c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '1.17-SNAPSHOT' + id 'java' id 'maven-publish' id 'signing' } @@ -12,12 +12,34 @@ base { } repositories { + maven { + name = 'Fabric maven' + url = 'https://maven.fabricmc.net/' + } + maven { + name = 'NeoForge maven' + url = 'https://maven.neoforged.net/releases/' + } + maven { + name = "Forge maven" + url = "https://maven.minecraftforge.net/" + } + maven { + name = 'Minecraft maven' + url = 'https://libraries.minecraft.net/' + } } dependencies { - minecraft "com.mojang:minecraft:${project.minecraft_version}" - mappings loom.officialMojangMappings() - modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + implementation("io.github.llamalad7:mixinextras-fabric:${project.mixinextras_version}") + compileOnly "net.fabricmc:fabric-loader:${project.fabric_version}" + //noinspection VulnerableLibrariesLocal + compileOnly "net.neoforged:neoforge:${project.neoforge_version}" + // istg why does forge need this many different dependencies to ensure getting all the needed classes, + // couldn't they use transitive dependencies like everyone? + compileOnly "net.minecraftforge:fmlloader:${project.forge_version}" + compileOnly "net.minecraftforge:forge:${project.forge_version}:universal" + compileOnly "net.minecraftforge:fmlcore:${project.forge_version}" } def targetJavaVersion = 17 diff --git a/gradle.properties b/gradle.properties index a88cd84..6b5b235 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,11 +1,9 @@ -# Done to increase the memory available to gradle. org.gradle.jvmargs=-Xmx1G -# Fabric Properties -# check these on https://modmuss50.me/fabric.html minecraft_version=1.20.1 -loader_version=0.18.6 -# Mod Properties -mod_version=1.0 +mixinextras_version=0.4.1 +fabric_version=0.18.6 +neoforge_version=20.2.3-beta +forge_version=1.20.1-47.0.19 +mod_version=1.1 maven_group=com.nerjal -maven_artifact=ModdingUtils - +maven_artifact=ModdingUtils \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index f91a4fe..c665ff9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,9 +1,5 @@ pluginManagement { repositories { - maven { - name = 'Fabric' - url = 'https://maven.fabricmc.net/' - } gradlePluginPortal() } } diff --git a/src/main/java/com/nerjal/moddingutils/compat/CompatMixinPlugin.java b/src/main/java/com/nerjal/moddingutils/compat/CompatMixinPlugin.java index 5ce2183..1648b8c 100644 --- a/src/main/java/com/nerjal/moddingutils/compat/CompatMixinPlugin.java +++ b/src/main/java/com/nerjal/moddingutils/compat/CompatMixinPlugin.java @@ -1,11 +1,7 @@ package com.nerjal.moddingutils.compat; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.ModContainer; -import net.fabricmc.loader.api.Version; -import net.fabricmc.loader.api.metadata.ModMetadata; -import net.fabricmc.loader.api.metadata.Person; -import net.fabricmc.loader.impl.util.version.VersionParser; +import com.nerjal.moddingutils.mod.ModVersion; +import com.nerjal.moddingutils.platform.PlatformService; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.slf4j.Logger; @@ -15,7 +11,6 @@ import org.spongepowered.asm.mixin.extensibility.IMixinInfo; import org.spongepowered.asm.service.MixinService; import java.util.*; -import java.util.stream.Collectors; public class CompatMixinPlugin implements IMixinConfigPlugin { Set loadedCompatModIds = new HashSet<>(); @@ -47,16 +42,16 @@ public class CompatMixinPlugin implements IMixinConfigPlugin { if (!node.desc.equals(Compat.class.descriptorString())) continue; Compat c = Compat.Impl.ofNode(node); List mods = Arrays.stream(c.mods()).sorted(Comparator.comparing(Mod::modId)).toList(); - Map modMap = new HashMap<>(); + Map modMap = new HashMap<>(); List missingMods = new ArrayList<>(); String modIds = String.join(", ", mods.stream().map(Mod::modId).toList()); for (Mod m : mods) { String modId = m.modId(); - Optional opt = FabricLoader.getInstance().getModContainer(modId); - if (opt.isEmpty()) { + Optional mod = PlatformService.getInstance().getMod(modId); + if (mod.isEmpty()) { missingMods.add(modId); } else { - modMap.put(m, opt.get()); + modMap.put(m, mod.get()); } } if (!missingMods.isEmpty()) { @@ -64,16 +59,14 @@ public class CompatMixinPlugin implements IMixinConfigPlugin { logger.info("Skipped compatibility mixin with mod(s) [{}]: mod(s) {} are not present", modIds, missingMods); } } - for (Map.Entry e : modMap.entrySet()) { - ModMetadata metadata = e.getValue().getMetadata(); - Set authors = metadata.getAuthors().stream().map(Person::getName).collect(Collectors.toSet()); - if (!authors.containsAll(Arrays.asList(e.getKey().authors()))) { - return false; + for (Map.Entry e : modMap.entrySet()) { + for (String author : e.getKey().authors()) { + if (!e.getValue().hasAuthor(author)) return false; } for (String v : e.getKey().versions()) { if (v.isBlank()) continue; - Version version = VersionParser.parse(v, true); - if (version.compareTo(metadata.getVersion()) != 0) return false; + ModVersion version = ModVersion.parse(v); + if (!e.getValue().matchesVersion(version)) return false; } } if (loadedCompatModIds.add(modIds)) { diff --git a/src/main/java/com/nerjal/moddingutils/extra/SubjectiveOpinion.java b/src/main/java/com/nerjal/moddingutils/extra/SubjectiveOpinion.java new file mode 100644 index 0000000..7f95afd --- /dev/null +++ b/src/main/java/com/nerjal/moddingutils/extra/SubjectiveOpinion.java @@ -0,0 +1,9 @@ +package com.nerjal.moddingutils.extra; + +@SubjectiveOpinion({ + "For the sake of leaving quirky comments in the code without affecting runtime", + "And it's funnier than just comments honestly" +}) +public @interface SubjectiveOpinion { + String[] value(); +} diff --git a/src/main/java/com/nerjal/moddingutils/mod/ModVersion.java b/src/main/java/com/nerjal/moddingutils/mod/ModVersion.java new file mode 100644 index 0000000..854179b --- /dev/null +++ b/src/main/java/com/nerjal/moddingutils/mod/ModVersion.java @@ -0,0 +1,28 @@ +package com.nerjal.moddingutils.mod; + +import org.jetbrains.annotations.Contract; + +/** + * This is straight-up copied from the Fabric loader api's versions, + * but since I did not find an easy way to only shadow the relevant + * classes, here we are. + */ +public interface ModVersion extends Comparable { + String getFriendlyString(); + + @Contract("null -> fail") + static ModVersion parse(String string) throws VersionParsingException { + if (string == null || string.isEmpty()) { + throw new VersionParsingException("Version must be a non-empty string."); + } + ModVersion version; + + try { + version = new SemanticModVersion(string); + } catch (VersionParsingException e) { + version = new StringModVersion(string); + } + + return version; + } +} diff --git a/src/main/java/com/nerjal/moddingutils/mod/SemanticModVersion.java b/src/main/java/com/nerjal/moddingutils/mod/SemanticModVersion.java new file mode 100644 index 0000000..9e2f2e1 --- /dev/null +++ b/src/main/java/com/nerjal/moddingutils/mod/SemanticModVersion.java @@ -0,0 +1,270 @@ +package com.nerjal.moddingutils.mod; + +import java.util.Arrays; +import java.util.Objects; +import java.util.Optional; +import java.util.StringTokenizer; +import java.util.regex.Pattern; + +public class SemanticModVersion implements ModVersion { + private static final Pattern DOT_SEPARATED_ID = Pattern.compile("|[-0-9A-Za-z]+(\\.[-0-9A-Za-z]+)*"); + private static final Pattern UNSIGNED_INTEGER = Pattern.compile("0|[1-9][0-9]*"); + private static final int COMPONENT_WILDCARD = Integer.MIN_VALUE; + private final int[] components; + private final String prerelease; + private final String build; + private String friendlyName; + + public SemanticModVersion(String version) throws VersionParsingException { + int buildDelimPos = version.indexOf('+'); + + if (buildDelimPos >= 0) { + build = version.substring(buildDelimPos + 1); + version = version.substring(0, buildDelimPos); + } else { + build = null; + } + + int dashDelimPos = version.indexOf('-'); + + if (dashDelimPos >= 0) { + prerelease = version.substring(dashDelimPos + 1); + version = version.substring(0, dashDelimPos); + } else { + prerelease = null; + } + + if (prerelease != null && !DOT_SEPARATED_ID.matcher(prerelease).matches()) { + throw new VersionParsingException("Invalid prerelease string '" + prerelease + "'!"); + } + + if (version.endsWith(".")) { + throw new VersionParsingException("Negative version number component found!"); + } else if (version.startsWith(".")) { + throw new VersionParsingException("Missing version component!"); + } + + String[] componentStrings = version.split("\\."); + + if (componentStrings.length < 1) { + throw new VersionParsingException("Did not provide version numbers!"); + } + + int[] components = new int[componentStrings.length]; + int firstWildcardIdx = -1; + + for (int i = 0; i < componentStrings.length; i++) { + String compStr = componentStrings[i]; + if (compStr.equals("x") || compStr.equals("X") || compStr.equals("*")) { + if (prerelease != null) { + throw new VersionParsingException("Pre-release versions are not allowed to use X-ranges!"); + } + + components[i] = COMPONENT_WILDCARD; + if (firstWildcardIdx < 0) firstWildcardIdx = i; + continue; + } else if (i > 0 && components[i - 1] == COMPONENT_WILDCARD) { + throw new VersionParsingException("Interjacent wildcard (1.x.2) are disallowed!"); + } + + if (compStr.trim().isEmpty()) { + throw new VersionParsingException("Missing version number component!"); + } + + try { + components[i] = Integer.parseInt(compStr); + + if (components[i] < 0) { + throw new VersionParsingException("Negative version number component '" + compStr + "'!"); + } + } catch (NumberFormatException e) { + throw new VersionParsingException("Could not parse version number component '" + compStr + "'!", e); + } + } + + if (components.length == 1 && components[0] == COMPONENT_WILDCARD) { + throw new VersionParsingException("Versions of form 'x' or 'X' not allowed!"); + } + + // strip extra wildcards (1.x.x -> 1.x) + if (firstWildcardIdx > 0 && components.length > firstWildcardIdx + 1) { + components = Arrays.copyOf(components, firstWildcardIdx + 1); + } + + this.components = components; + + buildFriendlyName(); + } + + public SemanticModVersion(int[] components, String prerelease, String build) { + if (components.length == 0 || components[0] == COMPONENT_WILDCARD) throw new IllegalArgumentException("Invalid components: "+Arrays.toString(components)); + + this.components = components; + this.prerelease = prerelease; + this.build = build; + + buildFriendlyName(); + } + + private void buildFriendlyName() { + StringBuilder fnBuilder = new StringBuilder(); + boolean first = true; + + for (int i : components) { + if (first) { + first = false; + } else { + fnBuilder.append('.'); + } + + if (i == COMPONENT_WILDCARD) { + fnBuilder.append('x'); + } else { + fnBuilder.append(i); + } + } + + if (prerelease != null) { + fnBuilder.append('-').append(prerelease); + } + + if (build != null) { + fnBuilder.append('+').append(build); + } + + friendlyName = fnBuilder.toString(); + } + + public int getVersionComponentCount() { + return components.length; + } + + public int getVersionComponent(int pos) { + if (pos < 0) { + throw new RuntimeException("Tried to access negative version number component!"); + } else if (pos >= components.length) { + // Repeat "x" if x-range, otherwise repeat "0". + return components[components.length - 1] == COMPONENT_WILDCARD ? COMPONENT_WILDCARD : 0; + } else { + return components[pos]; + } + } + + public int[] getVersionComponents() { + return components.clone(); + } + + public Optional getPrereleaseKey() { + return Optional.ofNullable(prerelease); + } + + public Optional getBuildKey() { + return Optional.ofNullable(build); + } + + public String getFriendlyString() { + return friendlyName; + } + + public boolean hasWildcard() { + for (int i : components) { + if (i < 0) { + return true; + } + } + return false; + } + + public boolean equalsComponentsExactly(SemanticModVersion other) { + for (int i = 0; i < Math.max(getVersionComponentCount(), other.getVersionComponentCount()); i++) { + if (getVersionComponent(i) != other.getVersionComponent(i)) { + return false; + } + } + + return true; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof SemanticModVersion other)) return false; + + if (!equalsComponentsExactly(other)) { + return false; + } + + return Objects.equals(prerelease, other.prerelease) && Objects.equals(build, other.build); + } + + @Override + public int hashCode() { + return Arrays.hashCode(components) * 73 + (prerelease != null ? prerelease.hashCode() * 11 : 0) + (build != null ? build.hashCode() : 0); + } + + @Override + public String toString() { + return getFriendlyString(); + } + + @Override + public int compareTo(ModVersion other) { + if (!(other instanceof SemanticModVersion o)) { + return getFriendlyString().compareTo(other.getFriendlyString()); + } + + for (int i = 0; i < Math.max(getVersionComponentCount(), o.getVersionComponentCount()); i++) { + int first = getVersionComponent(i); + int second = o.getVersionComponent(i); + + if (first == COMPONENT_WILDCARD || second == COMPONENT_WILDCARD) { + continue; + } + + int compare = Integer.compare(first, second); + if (compare != 0) return compare; + } + + Optional prereleaseA = getPrereleaseKey(); + Optional prereleaseB = o.getPrereleaseKey(); + + if (prereleaseA.isPresent() || prereleaseB.isPresent()) { + if (prereleaseA.isPresent() && prereleaseB.isPresent()) { + StringTokenizer prereleaseATokenizer = new StringTokenizer(prereleaseA.get(), "."); + StringTokenizer prereleaseBTokenizer = new StringTokenizer(prereleaseB.get(), "."); + + while (prereleaseATokenizer.hasMoreElements()) { + if (prereleaseBTokenizer.hasMoreElements()) { + String partA = prereleaseATokenizer.nextToken(); + String partB = prereleaseBTokenizer.nextToken(); + + if (UNSIGNED_INTEGER.matcher(partA).matches()) { + if (UNSIGNED_INTEGER.matcher(partB).matches()) { + int compare = Integer.compare(partA.length(), partB.length()); + if (compare != 0) return compare; + } else { + return -1; + } + } else { + if (UNSIGNED_INTEGER.matcher(partB).matches()) { + return 1; + } + } + + int compare = partA.compareTo(partB); + if (compare != 0) return compare; + } else { + return 1; + } + } + + return prereleaseBTokenizer.hasMoreElements() ? -1 : 0; + } else if (prereleaseA.isPresent()) { + return o.hasWildcard() ? 0 : -1; + } else { // prereleaseB.isPresent() + return hasWildcard() ? 0 : 1; + } + } else { + return 0; + } + } +} diff --git a/src/main/java/com/nerjal/moddingutils/mod/StringModVersion.java b/src/main/java/com/nerjal/moddingutils/mod/StringModVersion.java new file mode 100644 index 0000000..5d0bf35 --- /dev/null +++ b/src/main/java/com/nerjal/moddingutils/mod/StringModVersion.java @@ -0,0 +1,32 @@ +package com.nerjal.moddingutils.mod; + +public class StringModVersion implements ModVersion { + private final String version; + + public StringModVersion(String version) { + this.version = version; + } + + @Override + public String getFriendlyString() { + return version; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof StringModVersion other)) { + return false; + } + return version.equals(other.version); + } + + @Override + public int compareTo(ModVersion o) { + return getFriendlyString().compareTo(o.getFriendlyString()); + } + + @Override + public String toString() { + return version; + } +} diff --git a/src/main/java/com/nerjal/moddingutils/mod/VersionParsingException.java b/src/main/java/com/nerjal/moddingutils/mod/VersionParsingException.java new file mode 100644 index 0000000..bd1c47f --- /dev/null +++ b/src/main/java/com/nerjal/moddingutils/mod/VersionParsingException.java @@ -0,0 +1,19 @@ +package com.nerjal.moddingutils.mod; + +public class VersionParsingException extends Exception { + public VersionParsingException() { + super(); + } + + public VersionParsingException(Throwable t) { + super(t); + } + + public VersionParsingException(String s) { + super(s); + } + + public VersionParsingException(String s, Throwable t) { + super(s, t); + } +} diff --git a/src/main/java/com/nerjal/moddingutils/platform/Loader.java b/src/main/java/com/nerjal/moddingutils/platform/Loader.java new file mode 100644 index 0000000..2a4ef32 --- /dev/null +++ b/src/main/java/com/nerjal/moddingutils/platform/Loader.java @@ -0,0 +1,40 @@ +package com.nerjal.moddingutils.platform; + +import com.nerjal.moddingutils.extra.SubjectiveOpinion; + +public enum Loader { + FABRIC, + @Deprecated(since = "Since it has been led by a douche and a bunch of faggots") + @SubjectiveOpinion({ + "The sole fact that this modloader is still alive baffles me.", + "Who still uses it? Who hasn't moved on? Why?" + }) + FORGE, + @SubjectiveOpinion({ + "I am not saying we should use forge instead, but I don't believe this loader is a good option.", + "Thank you for listening to my TED talk." + }) + NEOFORGE, + @SubjectiveOpinion({ + "I know you're gonna say I forgot one. I don't care. I will not add it.", + "I have no reason to endorse a dead project whose leaders have hurt my friends." + }) + OTHER, + ; + + public static Loader parse(String s) { + String lower = s.toLowerCase(); + return switch (s.toLowerCase()) { + case "modlauncher" -> { + if (Loader.class.getClassLoader().getResource("net/neoforged/fml/ModLoader.class") == null) { + yield FORGE; + } else { + yield NEOFORGE; + } + } + case "launchwrapper" -> FORGE; + case "knot/fabric", "launchwrapper/fabric" -> FABRIC; + default -> OTHER; + }; + } +} diff --git a/src/main/java/com/nerjal/moddingutils/platform/PlatformService.java b/src/main/java/com/nerjal/moddingutils/platform/PlatformService.java new file mode 100644 index 0000000..c181ac0 --- /dev/null +++ b/src/main/java/com/nerjal/moddingutils/platform/PlatformService.java @@ -0,0 +1,90 @@ +package com.nerjal.moddingutils.platform; + +import com.nerjal.moddingutils.mod.ModVersion; +import com.nerjal.moddingutils.mod.VersionParsingException; +import org.jetbrains.annotations.Contract; +import org.spongepowered.asm.service.MixinService; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +public abstract sealed class PlatformService permits PlatformServiceFabric, PlatformServiceForge, PlatformServiceNeoForge, PlatformServiceOther { + + private static final Loader LOADER; + private static final Side SIDE; + private static PlatformService INSTANCE; + + static { + String name = MixinService.getService().getName(); + String side = MixinService.getService().getSideName(); + LOADER = Loader.parse(name); + SIDE = Side.parse(side); + + System.out.println(MixinService.getService().getName()); + System.out.println(MixinService.getService().getSideName()); + System.out.println(MixinService.getService().getClass()); + } + + @Contract(pure = true) + public static Loader getLoader() { + return LOADER; + } + + @Contract(pure = true) + public static Side getSide() { + return SIDE; + } + + public static PlatformService getInstance() { + if (INSTANCE == null) { + INSTANCE = switch (LOADER) { + case FABRIC -> new PlatformServiceFabric(); + case NEOFORGE -> new PlatformServiceNeoForge(); + case FORGE -> new PlatformServiceForge(); + default -> new PlatformServiceOther(); + }; + } + return INSTANCE; + } + + private final Map> modsMap = new HashMap<>(); + + public final Optional getMod(String modId) { + return modsMap.computeIfAbsent(modId, this::createModContainer); + } + + @Contract(pure = true) + protected abstract Optional createModContainer(String modId); + + public abstract static class PlatformModContainer { + private final String modId; + private final Set authors; + private final ModVersion version; + + protected PlatformModContainer(String modId, Set authors, String versionString) { + this.modId = modId; + this.authors = authors; + try { + this.version = ModVersion.parse(versionString); + } catch (VersionParsingException e) { + throw new RuntimeException(e); + } + } + + @Contract(pure = true) + public final boolean hasAuthor(String s) { + return authors.contains(s); + } + @Contract(pure = true) + public final boolean matchesVersion(ModVersion v) { + return version.compareTo(v) == 0; + } + + @Contract(pure = true) + public final String getModId() { + return modId; + } + } +} diff --git a/src/main/java/com/nerjal/moddingutils/platform/PlatformServiceFabric.java b/src/main/java/com/nerjal/moddingutils/platform/PlatformServiceFabric.java new file mode 100644 index 0000000..8fdb5cd --- /dev/null +++ b/src/main/java/com/nerjal/moddingutils/platform/PlatformServiceFabric.java @@ -0,0 +1,27 @@ +package com.nerjal.moddingutils.platform; + +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; +import net.fabricmc.loader.api.metadata.ModMetadata; +import net.fabricmc.loader.api.metadata.Person; + +import java.util.Optional; +import java.util.stream.Collectors; + +final class PlatformServiceFabric extends PlatformService { + @Override + protected Optional createModContainer(String modId) { + return FabricLoader.getInstance().getModContainer(modId) + .map(ModContainer::getMetadata) + .map(FabricModContainer::new); + } + + static final class FabricModContainer extends PlatformModContainer { + + private FabricModContainer(ModMetadata modMetadata) { + super(modMetadata.getId(), + modMetadata.getAuthors().stream().map(Person::getName).collect(Collectors.toSet()), + modMetadata.getVersion().getFriendlyString()); + } + } +} diff --git a/src/main/java/com/nerjal/moddingutils/platform/PlatformServiceForge.java b/src/main/java/com/nerjal/moddingutils/platform/PlatformServiceForge.java new file mode 100644 index 0000000..484be8d --- /dev/null +++ b/src/main/java/com/nerjal/moddingutils/platform/PlatformServiceForge.java @@ -0,0 +1,25 @@ +package com.nerjal.moddingutils.platform; + +import net.minecraftforge.fml.ModList; +import net.minecraftforge.forgespi.language.IModFileInfo; + +import java.util.Arrays; +import java.util.Optional; +import java.util.stream.Collectors; + +final class PlatformServiceForge extends PlatformService { + @Override + protected Optional createModContainer(String modId) { + return Optional.ofNullable(ModList.get().getModFileById(modId)).map(i -> new ForgeModContainer(modId, i)); + } + + static final class ForgeModContainer extends PlatformModContainer { + + ForgeModContainer(String modId, IModFileInfo modInfo) { + super(modId, Arrays.stream(modInfo.getConfig().getConfigElement("authors") + .map(Object::toString).orElse("").split("[\\w,+;]+")) + .collect(Collectors.toSet()), + modInfo.versionString()); + } + } +} diff --git a/src/main/java/com/nerjal/moddingutils/platform/PlatformServiceNeoForge.java b/src/main/java/com/nerjal/moddingutils/platform/PlatformServiceNeoForge.java new file mode 100644 index 0000000..11ef3ff --- /dev/null +++ b/src/main/java/com/nerjal/moddingutils/platform/PlatformServiceNeoForge.java @@ -0,0 +1,24 @@ +package com.nerjal.moddingutils.platform; + +import net.neoforged.fml.ModList; +import net.neoforged.neoforgespi.language.IModFileInfo; + +import java.util.Arrays; +import java.util.Optional; +import java.util.stream.Collectors; + +final class PlatformServiceNeoForge extends PlatformService { + @Override + protected Optional createModContainer(String modId) { + return Optional.ofNullable(ModList.get().getModFileById(modId)).map(i -> new NeoForgeModContainer(modId, i)); + } + + static final class NeoForgeModContainer extends PlatformModContainer { + NeoForgeModContainer(String modId, IModFileInfo modInfo) { + super(modId, Arrays.stream(modInfo.getConfig().getConfigElement("authors") + .map(Object::toString).orElse("").split("[\\w,+;]+")) + .collect(Collectors.toSet()), + modInfo.versionString()); + } + } +} diff --git a/src/main/java/com/nerjal/moddingutils/platform/PlatformServiceOther.java b/src/main/java/com/nerjal/moddingutils/platform/PlatformServiceOther.java new file mode 100644 index 0000000..ee6cc5c --- /dev/null +++ b/src/main/java/com/nerjal/moddingutils/platform/PlatformServiceOther.java @@ -0,0 +1,13 @@ +package com.nerjal.moddingutils.platform; + +import com.nerjal.moddingutils.extra.SubjectiveOpinion; + +import java.util.Optional; + +@SubjectiveOpinion("Unknown or voluntarily unsupported loaders") +final class PlatformServiceOther extends PlatformService { + @Override + protected Optional createModContainer(String modId) { + return Optional.empty(); + } +} diff --git a/src/main/java/com/nerjal/moddingutils/platform/Side.java b/src/main/java/com/nerjal/moddingutils/platform/Side.java new file mode 100644 index 0000000..b1f256d --- /dev/null +++ b/src/main/java/com/nerjal/moddingutils/platform/Side.java @@ -0,0 +1,16 @@ +package com.nerjal.moddingutils.platform; + +public enum Side { + CLIENT, + SERVER, + UNKNOWN, + ; + + public static Side parse(String s) { + return switch (s.toLowerCase()) { + case "client" -> CLIENT; + case "server" -> SERVER; + default -> UNKNOWN; + }; + } +} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 0000000..d7a633d --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,14 @@ +module ModdingUtils { + requires net.fabricmc.loader; + requires org.jetbrains.annotations; + requires org.objectweb.asm.tree; + requires org.slf4j; + requires org.spongepowered.mixin; + requires fmlcore; + requires net.minecraftforge.forgespi; + requires net.neoforged.neoforgespi; + requires fml_core; + opens com.nerjal.moddingutils.compat; + opens com.nerjal.moddingutils.mod; + opens com.nerjal.moddingutils.platform; +} \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json deleted file mode 100644 index 34db955..0000000 --- a/src/main/resources/fabric.mod.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "schemaVersion": 1, - "id": "moddingutils", - "version": "${version}", - "name": "ModdingUtils", - "description": "Various modding utilities to share and use freely", - "authors": [ - { - "name": "Nerjal Nosk", - "contact": { - "homepage": "https://codeberg.org/NerjalNosk" - } - } - ], - "contact": { - "homepage": "https://codeberg.org/NerjalNosk/ModdingUtils" - }, - "license": "LGPL-3.0", - "environment": "*", - "entrypoints": { - }, - "mixins": [ - ], - "depends": { - "fabricloader": "*", - "java": "${java_version}" - }, - "custom": { - "modmenu": { - "badges": [ - "library" - ] - } - } -}