Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5192a5258b
|
||
|
|
9f5b16756a
|
||
|
|
5188e4dcf5
|
No files matched your search
@@ -0,0 +1,34 @@
|
||||
# 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
|
||||
`com.nerjal.moddingutils.compat.CompatMixinPLugin` and will use the `Compat` annotation
|
||||
to allow you to make mixins only be applied with their respective dependencies are
|
||||
loaded.
|
||||
|
||||
Dependencies need a mod ID at least, but can also include filtering base on authors names
|
||||
as well as version ranges.
|
||||
|
||||
# Usage
|
||||
|
||||
Add the `https://maven.nerjal.com/releases/` maven repository to your project,
|
||||
and import the `com.nerjal:ModdingUtils:<VERSION>` artifact in your project.
|
||||
|
||||
You are free to bundle this project into yours.
|
||||
|
||||
Don't hesitate to add a dependency to "the same main version as yours" in your project
|
||||
manifest (e.g. `"moddingutils": "~${moddingutils_version}"`) to avoid trying to load
|
||||
with breaking changes brought by some other project including a more recent version of
|
||||
ModdingUtils.
|
||||
|
||||
## Latest version
|
||||
|
||||
[1.1](https://maven.nerjal.com/#/releases/com/nerjal/ModdingUtils/1.1)
|
||||
+28
-6
@@ -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
|
||||
@@ -61,7 +83,7 @@ publishing {
|
||||
from components.java
|
||||
|
||||
it.artifactId = project.maven_artifact
|
||||
it.version = "${project.mod_version}-${project.minecraft_version}"
|
||||
it.version = project.version
|
||||
|
||||
pom {
|
||||
packaging = "jar"
|
||||
@@ -100,7 +122,7 @@ publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name = "Nerjal"
|
||||
url = "https://maven.nerjal.com/snapshots"
|
||||
url = "https://maven.nerjal.com/releases"
|
||||
credentials(PasswordCredentials.class)
|
||||
authentication {
|
||||
it.create("basic", BasicAuthentication.class)
|
||||
|
||||
+5
-7
@@ -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
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven {
|
||||
name = 'Fabric'
|
||||
url = 'https://maven.fabricmc.net/'
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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<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<>();
|
||||
String modIds = String.join(", ", mods.stream().map(Mod::modId).toList());
|
||||
for (Mod m : mods) {
|
||||
String modId = m.modId();
|
||||
Optional<ModContainer> opt = FabricLoader.getInstance().getModContainer(modId);
|
||||
if (opt.isEmpty()) {
|
||||
Optional<PlatformService.PlatformModContainer> 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<Mod, ModContainer> e : modMap.entrySet()) {
|
||||
ModMetadata metadata = e.getValue().getMetadata();
|
||||
Set<String> authors = metadata.getAuthors().stream().map(Person::getName).collect(Collectors.toSet());
|
||||
if (!authors.containsAll(Arrays.asList(e.getKey().authors()))) {
|
||||
return false;
|
||||
for (Map.Entry<Mod, PlatformService.PlatformModContainer> 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)) {
|
||||
|
||||
@@ -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