Now loader-agnostic
This commit is contained in:
19 files changed
+657
-75
No files matched your search
@@ -1,11 +1,12 @@
|
|||||||
# Supported platforms
|
|
||||||
|
|
||||||
Fabric (required for platform mod resolution)
|
|
||||||
|
|
||||||
Java >= 17
|
|
||||||
|
|
||||||
# Features
|
# 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
|
## Modding compat
|
||||||
|
|
||||||
A mod compatibility mixin plugin is made available at
|
A mod compatibility mixin plugin is made available at
|
||||||
@@ -30,4 +31,4 @@ ModdingUtils.
|
|||||||
|
|
||||||
## Latest version
|
## Latest version
|
||||||
|
|
||||||
[1.0](https://maven.nerjal.com/#/releases/com/nerjal/ModdingUtils/1.0)
|
[1.1](https://maven.nerjal.com/#/releases/com/nerjal/ModdingUtils/1.1)
|
||||||
+26
-4
@@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'fabric-loom' version '1.17-SNAPSHOT'
|
id 'java'
|
||||||
id 'maven-publish'
|
id 'maven-publish'
|
||||||
id 'signing'
|
id 'signing'
|
||||||
}
|
}
|
||||||
@@ -12,12 +12,34 @@ base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
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 {
|
dependencies {
|
||||||
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
implementation("io.github.llamalad7:mixinextras-fabric:${project.mixinextras_version}")
|
||||||
mappings loom.officialMojangMappings()
|
compileOnly "net.fabricmc:fabric-loader:${project.fabric_version}"
|
||||||
modImplementation "net.fabricmc:fabric-loader:${project.loader_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
|
def targetJavaVersion = 17
|
||||||
|
|||||||
+5
-7
@@ -1,11 +1,9 @@
|
|||||||
# Done to increase the memory available to gradle.
|
|
||||||
org.gradle.jvmargs=-Xmx1G
|
org.gradle.jvmargs=-Xmx1G
|
||||||
# Fabric Properties
|
|
||||||
# check these on https://modmuss50.me/fabric.html
|
|
||||||
minecraft_version=1.20.1
|
minecraft_version=1.20.1
|
||||||
loader_version=0.18.6
|
mixinextras_version=0.4.1
|
||||||
# Mod Properties
|
fabric_version=0.18.6
|
||||||
mod_version=1.0
|
neoforge_version=20.2.3-beta
|
||||||
|
forge_version=1.20.1-47.0.19
|
||||||
|
mod_version=1.1
|
||||||
maven_group=com.nerjal
|
maven_group=com.nerjal
|
||||||
maven_artifact=ModdingUtils
|
maven_artifact=ModdingUtils
|
||||||
|
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
pluginManagement {
|
pluginManagement {
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
|
||||||
name = 'Fabric'
|
|
||||||
url = 'https://maven.fabricmc.net/'
|
|
||||||
}
|
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,7 @@
|
|||||||
package com.nerjal.moddingutils.compat;
|
package com.nerjal.moddingutils.compat;
|
||||||
|
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import com.nerjal.moddingutils.mod.ModVersion;
|
||||||
import net.fabricmc.loader.api.ModContainer;
|
import com.nerjal.moddingutils.platform.PlatformService;
|
||||||
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 org.objectweb.asm.tree.AnnotationNode;
|
import org.objectweb.asm.tree.AnnotationNode;
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -15,7 +11,6 @@ import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
|||||||
import org.spongepowered.asm.service.MixinService;
|
import org.spongepowered.asm.service.MixinService;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class CompatMixinPlugin implements IMixinConfigPlugin {
|
public class CompatMixinPlugin implements IMixinConfigPlugin {
|
||||||
Set<String> loadedCompatModIds = new HashSet<>();
|
Set<String> loadedCompatModIds = new HashSet<>();
|
||||||
@@ -47,16 +42,16 @@ public class CompatMixinPlugin implements IMixinConfigPlugin {
|
|||||||
if (!node.desc.equals(Compat.class.descriptorString())) continue;
|
if (!node.desc.equals(Compat.class.descriptorString())) continue;
|
||||||
Compat c = Compat.Impl.ofNode(node);
|
Compat c = Compat.Impl.ofNode(node);
|
||||||
List<Mod> mods = Arrays.stream(c.mods()).sorted(Comparator.comparing(Mod::modId)).toList();
|
List<Mod> mods = Arrays.stream(c.mods()).sorted(Comparator.comparing(Mod::modId)).toList();
|
||||||
Map<Mod, ModContainer> modMap = new HashMap<>();
|
Map<Mod, PlatformService.PlatformModContainer> modMap = new HashMap<>();
|
||||||
List<String> missingMods = new ArrayList<>();
|
List<String> missingMods = new ArrayList<>();
|
||||||
String modIds = String.join(", ", mods.stream().map(Mod::modId).toList());
|
String modIds = String.join(", ", mods.stream().map(Mod::modId).toList());
|
||||||
for (Mod m : mods) {
|
for (Mod m : mods) {
|
||||||
String modId = m.modId();
|
String modId = m.modId();
|
||||||
Optional<ModContainer> opt = FabricLoader.getInstance().getModContainer(modId);
|
Optional<PlatformService.PlatformModContainer> mod = PlatformService.getInstance().getMod(modId);
|
||||||
if (opt.isEmpty()) {
|
if (mod.isEmpty()) {
|
||||||
missingMods.add(modId);
|
missingMods.add(modId);
|
||||||
} else {
|
} else {
|
||||||
modMap.put(m, opt.get());
|
modMap.put(m, mod.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!missingMods.isEmpty()) {
|
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);
|
logger.info("Skipped compatibility mixin with mod(s) [{}]: mod(s) {} are not present", modIds, missingMods);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Map.Entry<Mod, ModContainer> e : modMap.entrySet()) {
|
for (Map.Entry<Mod, PlatformService.PlatformModContainer> e : modMap.entrySet()) {
|
||||||
ModMetadata metadata = e.getValue().getMetadata();
|
for (String author : e.getKey().authors()) {
|
||||||
Set<String> authors = metadata.getAuthors().stream().map(Person::getName).collect(Collectors.toSet());
|
if (!e.getValue().hasAuthor(author)) return false;
|
||||||
if (!authors.containsAll(Arrays.asList(e.getKey().authors()))) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
for (String v : e.getKey().versions()) {
|
for (String v : e.getKey().versions()) {
|
||||||
if (v.isBlank()) continue;
|
if (v.isBlank()) continue;
|
||||||
Version version = VersionParser.parse(v, true);
|
ModVersion version = ModVersion.parse(v);
|
||||||
if (version.compareTo(metadata.getVersion()) != 0) return false;
|
if (!e.getValue().matchesVersion(version)) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (loadedCompatModIds.add(modIds)) {
|
if (loadedCompatModIds.add(modIds)) {
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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<ModVersion> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String> getPrereleaseKey() {
|
||||||
|
return Optional.ofNullable(prerelease);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> 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<String> prereleaseA = getPrereleaseKey();
|
||||||
|
Optional<String> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String, Optional<PlatformModContainer>> modsMap = new HashMap<>();
|
||||||
|
|
||||||
|
public final Optional<PlatformModContainer> getMod(String modId) {
|
||||||
|
return modsMap.computeIfAbsent(modId, this::createModContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
|
protected abstract Optional<PlatformModContainer> createModContainer(String modId);
|
||||||
|
|
||||||
|
public abstract static class PlatformModContainer {
|
||||||
|
private final String modId;
|
||||||
|
private final Set<String> authors;
|
||||||
|
private final ModVersion version;
|
||||||
|
|
||||||
|
protected PlatformModContainer(String modId, Set<String> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<PlatformModContainer> 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<PlatformModContainer> 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<PlatformModContainer> 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<PlatformModContainer> createModContainer(String modId) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user