v1
This commit is contained in:
10 files changed
+1326
No files matched your search
@@ -0,0 +1,73 @@
|
||||
package com.nerjal.moddingutils.compat;
|
||||
|
||||
import org.objectweb.asm.tree.AnnotationNode;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Compat {
|
||||
Mod[] mods();
|
||||
|
||||
@SuppressWarnings("ClassExplicitlyAnnotation")
|
||||
class Impl implements Compat {
|
||||
|
||||
public static Impl ofNode(AnnotationNode node) {
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
if (!node.desc.equals(Compat.class.descriptorString())) {
|
||||
throw new UnsupportedOperationException("Cannot create an implementation of "+Compat.class+" from a "+node.desc+" node");
|
||||
}
|
||||
List<Object> l = node.values;
|
||||
Mod[] mods = null;
|
||||
for (int i = 0; i < l.size(); i++) {
|
||||
String key = (String) l.get(i++);
|
||||
if (key.equals("mods")) {
|
||||
//noinspection unchecked
|
||||
List<AnnotationNode> modNodes = (List<AnnotationNode>) l.get(i);
|
||||
mods = new Mod[modNodes.size()];
|
||||
for (int j = 0; j < modNodes.size(); j++)
|
||||
mods[j] = Mod.Impl.ofNode(modNodes.get(j));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mods == null) {
|
||||
throw new IllegalArgumentException("No 'mods' value provided");
|
||||
}
|
||||
return new Impl(clearDupes(mods));
|
||||
}
|
||||
|
||||
static Mod[] clearDupes(Mod[] mods) {
|
||||
Mod[] out = new Mod[mods.length];
|
||||
int s = 0;
|
||||
base: for (Mod m : mods) {
|
||||
for (int x = 0; x < s; x++) {
|
||||
if (m.modId().equals(out[x].modId())) {
|
||||
continue base;
|
||||
}
|
||||
}
|
||||
out[s++] = m;
|
||||
}
|
||||
return Arrays.copyOf(out, s);
|
||||
}
|
||||
|
||||
private final Mod[] mods;
|
||||
|
||||
public Impl(Mod[] mods) {
|
||||
this.mods = mods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mod[] mods() {
|
||||
return Arrays.copyOf(mods, mods.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
return Compat.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
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 org.objectweb.asm.tree.AnnotationNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||
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<>();
|
||||
Set<String> skippedModCompats = new HashSet<>();
|
||||
Logger logger;
|
||||
|
||||
@Override
|
||||
public void onLoad(String mixinPackage) {
|
||||
String[] split = mixinPackage.split("\\.");
|
||||
int x = split.length;
|
||||
String s = split[--x];
|
||||
while (s.isBlank() && x > 0) s = split[--x];
|
||||
logger = LoggerFactory.getLogger("["+s+"/MixinCompat]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRefMapperConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
|
||||
try {
|
||||
List<AnnotationNode> list = MixinService.getService().getBytecodeProvider().getClassNode(mixinClassName).visibleAnnotations;
|
||||
if (list == null) {
|
||||
return true;
|
||||
}
|
||||
for (AnnotationNode node : list) {
|
||||
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<>();
|
||||
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()) {
|
||||
missingMods.add(modId);
|
||||
} else {
|
||||
modMap.put(m, opt.get());
|
||||
}
|
||||
}
|
||||
if (!missingMods.isEmpty()) {
|
||||
if (skippedModCompats.add(modIds)) {
|
||||
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 (String v : e.getKey().versions()) {
|
||||
if (v.isBlank()) continue;
|
||||
Version version = VersionParser.parse(v, true);
|
||||
if (version.compareTo(metadata.getVersion()) != 0) return false;
|
||||
}
|
||||
}
|
||||
if (loadedCompatModIds.add(modIds)) {
|
||||
logger.info("Loaded compat for mods {}", modIds);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMixins() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.nerjal.moddingutils.compat;
|
||||
|
||||
import org.objectweb.asm.tree.AnnotationNode;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Target({})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Mod {
|
||||
/**
|
||||
* A required mod's ID for compatibility mixin application
|
||||
*/
|
||||
String modId();
|
||||
|
||||
/**
|
||||
* A required mod's authors for compatibility mixin application
|
||||
*/
|
||||
String[] authors() default {};
|
||||
|
||||
/**
|
||||
* A required mod's version ranges for compatibility mixin application.
|
||||
* <p>
|
||||
* This uses the same version system as the {@code fabric.mod.json} files, so
|
||||
* it is not required to include every valid version.
|
||||
*/
|
||||
String[] versions() default {};
|
||||
|
||||
@SuppressWarnings("ClassExplicitlyAnnotation")
|
||||
class Impl implements Mod {
|
||||
|
||||
public static Impl ofNode(AnnotationNode node) {
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
if (!node.desc.equals(Mod.class.descriptorString())) {
|
||||
throw new UnsupportedOperationException("Cannot create an implementation of "+Mod.class+" from a "+node.desc+" node");
|
||||
}
|
||||
List<Object> l = node.values;
|
||||
String modId = null;
|
||||
String[] authors = new String[0];
|
||||
String[] versions = new String[0];
|
||||
for (int i = 0; i < l.size(); i++) {
|
||||
String key = (String) l.get(i++);
|
||||
Object v = l.get(i);
|
||||
switch (key) {
|
||||
case "modId":
|
||||
modId = (String) v;
|
||||
break;
|
||||
case "authors":
|
||||
authors = (String[]) v;
|
||||
break;
|
||||
case "versions":
|
||||
versions = (String[]) v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (modId == null) {
|
||||
throw new IllegalArgumentException("No 'modId' annotation value provided");
|
||||
}
|
||||
return new Impl(modId, authors, versions);
|
||||
}
|
||||
|
||||
private final String modId;
|
||||
private final String[] authors;
|
||||
private final String[] versions;
|
||||
|
||||
public Impl(String modId, String[] authors, String[] versions) {
|
||||
this.modId = modId;
|
||||
this.authors = authors;
|
||||
this.versions = versions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String modId() {
|
||||
return this.modId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] authors() {
|
||||
return Arrays.copyOf(authors, authors.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] versions() {
|
||||
return Arrays.copyOf(versions, versions.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
return Mod.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"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