aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-08-10 01:59:34 +0200
committerLinnea Gräf <nea@nea.moe>2024-08-12 21:02:44 +0200
commit3c7e6b6177de6ef3cff8a46bb1726466a299cdde (patch)
tree2ebc75e705b5422a68d5d7f04d88e3d8934cf02d
parent1606188d9ad65c66e9d873497ea3271dbdadaf77 (diff)
downloadfirmament-3c7e6b6177de6ef3cff8a46bb1726466a299cdde.tar.gz
firmament-3c7e6b6177de6ef3cff8a46bb1726466a299cdde.tar.bz2
firmament-3c7e6b6177de6ef3cff8a46bb1726466a299cdde.zip
Add indigo support to custom block textures
-rw-r--r--REUSE.toml5
-rw-r--r--build.gradle.kts89
-rw-r--r--gradle/libs.versions.toml2
-rw-r--r--gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--javaplugin/build.gradle.kts16
-rw-r--r--javaplugin/src/main/java/moe/nea/firmament/javaplugin/InitReplacer.java73
-rw-r--r--javaplugin/src/main/java/moe/nea/firmament/javaplugin/IntermediaryMethodReplacer.java72
-rw-r--r--javaplugin/src/main/java/moe/nea/firmament/javaplugin/IntermediaryNameResolutionPlugin.java25
-rw-r--r--javaplugin/src/main/java/moe/nea/firmament/javaplugin/IntermediaryNameResolutionTask.java44
-rw-r--r--javaplugin/src/main/java/moe/nea/firmament/javaplugin/MappingTree.java48
-rw-r--r--javaplugin/src/main/java/moe/nea/firmament/javaplugin/Utils.java121
-rw-r--r--javaplugin/src/main/resources/META-INF/services/com.sun.source.util.Plugin1
-rw-r--r--settings.gradle.kts1
-rw-r--r--src/main/java/moe/nea/firmament/init/ClientPlayerRiser.java13
-rw-r--r--src/main/java/moe/nea/firmament/init/EarlyRiser.java1
-rw-r--r--src/main/java/moe/nea/firmament/init/HandledScreenRiser.java8
-rw-r--r--src/main/java/moe/nea/firmament/init/Intermediary.java63
-rw-r--r--src/main/java/moe/nea/firmament/init/IntermediaryName.java21
-rw-r--r--src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java116
-rw-r--r--src/main/kotlin/moe/nea/firmament/Firmament.kt3
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt9
-rw-r--r--src/main/resources/firmament.accesswidener2
22 files changed, 689 insertions, 46 deletions
diff --git a/REUSE.toml b/REUSE.toml
index 19a1083..1cafccf 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -31,3 +31,8 @@ SPDX-FileCopyrightText = ["Linnea Gräf <nea@nea.moe>", "Firmament Contributors"
path = "**/*.gradle.kts"
SPDX-License-Identifier = "CC0-1.0"
SPDX-FileCopyrightText = ["Linnea Gräf <nea@nea.moe>", "Firmament Contributors"]
+
+[[annotations]]
+path = ["**/META-INF/services/*"]
+SPDX-License-Identifier = "CC0-1.0"
+SPDX-FileCopyrightText = ["Linnea Gräf <nea@nea.moe>", "Firmament Contributors"]
diff --git a/build.gradle.kts b/build.gradle.kts
index 24b263e..12b8bc6 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -7,6 +7,7 @@
*/
import moe.nea.licenseextractificator.LicenseDiscoveryTask
+import net.fabricmc.loom.LoomGradleExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
@@ -38,46 +39,49 @@ compileTestKotlin.kotlinOptions {
jvmTarget = "21"
}
-repositories {
- maven("https://maven.terraformersmc.com/releases/")
- maven("https://maven.shedaniel.me")
- maven("https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1")
- maven("https://api.modrinth.com/maven") {
- content {
- includeGroup("maven.modrinth")
- }
- }
- maven("https://repo.sleeping.town") {
- content {
- includeGroup("com.unascribed")
- }
- }
- ivy("https://github.com/HotswapProjects/HotswapAgent/releases/download") {
- patternLayout {
- artifact("[revision]/[artifact]-[revision].[ext]")
+allprojects {
+ repositories {
+ mavenCentral()
+ maven("https://maven.terraformersmc.com/releases/")
+ maven("https://maven.shedaniel.me")
+ maven("https://maven.fabricmc.net")
+ maven("https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1")
+ maven("https://api.modrinth.com/maven") {
+ content {
+ includeGroup("maven.modrinth")
+ }
}
- content {
- includeGroup("virtual.github.hotswapagent")
+ maven("https://repo.sleeping.town") {
+ content {
+ includeGroup("com.unascribed")
+ }
}
- metadataSources {
- artifact()
+ ivy("https://github.com/HotswapProjects/HotswapAgent/releases/download") {
+ patternLayout {
+ artifact("[revision]/[artifact]-[revision].[ext]")
+ }
+ content {
+ includeGroup("virtual.github.hotswapagent")
+ }
+ metadataSources {
+ artifact()
+ }
}
- }
- maven("https://server.bbkr.space/artifactory/libs-release")
- maven("https://repo.nea.moe/releases")
- maven("https://maven.notenoughupdates.org/releases")
- maven("https://repo.nea.moe/mirror")
- maven("https://jitpack.io/") {
- content {
- includeGroupByRegex("(com|io)\\.github\\..+")
- excludeModule("io.github.cottonmc", "LibGui")
+ maven("https://server.bbkr.space/artifactory/libs-release")
+ maven("https://repo.nea.moe/releases")
+ maven("https://maven.notenoughupdates.org/releases")
+ maven("https://repo.nea.moe/mirror")
+ maven("https://jitpack.io/") {
+ content {
+ includeGroupByRegex("(com|io)\\.github\\..+")
+ excludeModule("io.github.cottonmc", "LibGui")
+ }
}
+ maven("https://repo.hypixel.net/repository/Hypixel/")
+ maven("https://maven.azureaaron.net/snapshots")
+ mavenLocal()
}
- maven( "https://repo.hypixel.net/repository/Hypixel/")
- maven("https://maven.azureaaron.net/snapshots")
- mavenLocal()
}
-
kotlin {
sourceSets.all {
languageSettings {
@@ -124,6 +128,8 @@ dependencies {
modImplementation(libs.moulconfig)
modImplementation(libs.manninghamMills)
modCompileOnly(libs.explosiveenhancement)
+ compileOnly(project(":javaplugin"))
+ annotationProcessor(project(":javaplugin"))
include(libs.manninghamMills)
include(libs.moulconfig)
@@ -209,8 +215,21 @@ loom {
}
tasks.withType<JavaCompile> {
+ this.sourceCompatibility = "21"
+ this.targetCompatibility = "21"
options.encoding = "UTF-8"
- options.release.set(21)
+ val module = "ALL-UNNAMED"
+ options.forkOptions.jvmArgs!!.addAll(listOf(
+ "--add-exports=jdk.compiler/com.sun.tools.javac.util=$module",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.comp=$module",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.tree=$module",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.api=$module",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.code=$module",
+ ))
+ options.isFork = true
+ afterEvaluate {
+ options.compilerArgs.add("-Xplugin:IntermediaryNameReplacement mappingFile=${LoomGradleExtension.get(project).mappingsFile.absolutePath} sourceNs=named")
+ }
}
tasks.jar {
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 80731a7..d4820e3 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -71,7 +71,7 @@ runtime_required = [
runtime_optional = [
"devauth",
"freecammod",
- "sodium",
+# "sodium",
# "qolify",
# "citresewn",
# "ncr",
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 4c78103..0d93904 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -4,6 +4,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/javaplugin/build.gradle.kts b/javaplugin/build.gradle.kts
new file mode 100644
index 0000000..9707639
--- /dev/null
+++ b/javaplugin/build.gradle.kts
@@ -0,0 +1,16 @@
+plugins {
+ java
+}
+dependencies {
+ implementation("net.fabricmc:stitch:0.6.2")
+}
+tasks.withType(JavaCompile::class) {
+ val module = "ALL-UNNAMED"
+ options.compilerArgs.addAll(listOf(
+ "--add-exports=jdk.compiler/com.sun.tools.javac.util=$module",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.comp=$module",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.tree=$module",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.api=$module",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.code=$module",
+ ))
+}
diff --git a/javaplugin/src/main/java/moe/nea/firmament/javaplugin/InitReplacer.java b/javaplugin/src/main/java/moe/nea/firmament/javaplugin/InitReplacer.java
new file mode 100644
index 0000000..a0d28ab
--- /dev/null
+++ b/javaplugin/src/main/java/moe/nea/firmament/javaplugin/InitReplacer.java
@@ -0,0 +1,73 @@
+package moe.nea.firmament.javaplugin;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreeScanner;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Names;
+
+public class InitReplacer extends TreeScanner<Void, Void> {
+ private final MappingTree mappingTree;
+ private final TreeMaker treeMaker;
+ private final Names names;
+ private final IntermediaryNameResolutionTask plugin;
+ private Symbol.ClassSymbol classTree;
+ private CompilationUnitTree compilationUnitTree;
+
+ public InitReplacer(MappingTree mappingTree, IntermediaryNameResolutionTask plugin) {
+ this.mappingTree = mappingTree;
+ this.treeMaker = plugin.treeMaker;
+ this.names = plugin.names;
+ this.plugin = plugin;
+ }
+
+ @Override
+ public Void visitClass(ClassTree node, Void unused) {
+ this.classTree = plugin.utils.getSymbol(node);
+ return super.visitClass(node, unused);
+ }
+
+ @Override
+ public Void visitCompilationUnit(CompilationUnitTree node, Void unused) {
+ this.compilationUnitTree = node;
+ return super.visitCompilationUnit(node, unused);
+ }
+
+ @Override
+ public Void visitVariable(VariableTree node, Void unused) {
+ var annotation = node
+ .getModifiers().getAnnotations()
+ .stream()
+ .filter(it -> it.getAnnotationType().toString().equals("IntermediaryName")) // Crazy type-safety!
+ .findAny();
+ if (annotation.isEmpty())
+ return super.visitVariable(node, unused);
+ var jcAnnotation = (JCTree.JCAnnotation) annotation.get();
+ var jcNode = (JCTree.JCVariableDecl) node;
+ if (node.getInitializer() != null) {
+ plugin.utils.reportError(
+ compilationUnitTree.getSourceFile(),
+ jcNode.getInitializer(),
+ "Providing an initializer for a variable is illegal for @IntermediaryName annotated fields"
+ );
+ return super.visitVariable(node, unused);
+ }
+ var target = plugin.utils.getAnnotationValue(jcAnnotation, "value");
+ var targetClass = plugin.utils.resolveClassLiteralExpression(target).tsym.flatName().toString();
+ var intermediaryClass = mappingTree.resolveClassToIntermediary(targetClass);
+ var remapper = treeMaker.Select(treeMaker.This(classTree.type), names.fromString("remapper"));
+ var remappingCall = treeMaker.Apply(
+ List.nil(),
+ treeMaker.Select(remapper, names.fromString("mapClassName")),
+ List.of(treeMaker.Literal("intermediary"),
+ treeMaker.Literal(intermediaryClass)));
+ jcNode.init = remappingCall;
+ jcNode.mods.annotations = List.filter(jcNode.mods.annotations, jcAnnotation);
+ return super.visitVariable(node, unused);
+ }
+
+}
diff --git a/javaplugin/src/main/java/moe/nea/firmament/javaplugin/IntermediaryMethodReplacer.java b/javaplugin/src/main/java/moe/nea/firmament/javaplugin/IntermediaryMethodReplacer.java
new file mode 100644
index 0000000..d3040b7
--- /dev/null
+++ b/javaplugin/src/main/java/moe/nea/firmament/javaplugin/IntermediaryMethodReplacer.java
@@ -0,0 +1,72 @@
+package moe.nea.firmament.javaplugin;
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.util.TreeScanner;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.util.List;
+
+import javax.tools.JavaFileObject;
+
+public class IntermediaryMethodReplacer extends TreeScanner<Void, Void> {
+ private final MappingTree mappings;
+ private final IntermediaryNameResolutionTask plugin;
+ private JavaFileObject sourceFile;
+ private CompilationUnitTree compilationUnit;
+
+ public IntermediaryMethodReplacer(MappingTree mappings, IntermediaryNameResolutionTask plugin) {
+ this.mappings = mappings;
+ this.plugin = plugin;
+ }
+
+
+ @Override
+ public Void visitCompilationUnit(CompilationUnitTree node, Void unused) {
+ sourceFile = node.getSourceFile();
+ compilationUnit = node;
+ return super.visitCompilationUnit(node, unused);
+ }
+
+ public void replaceMethodName(JCTree.JCMethodInvocation node) {
+ var select = node.getMethodSelect();
+ if (!(select instanceof JCTree.JCFieldAccess fieldAccess)) return;
+ if (!fieldAccess.name.contentEquals("methodName")) return;
+ if (!(node.args.head instanceof JCTree.JCMemberReference methodReference)) {
+ plugin.utils.reportError(sourceFile, node, "Please provide a Class::method reference directly (and nothing else)");
+ return;
+ }
+ var clearName = methodReference.name.toString();
+ var classRef = methodReference.expr;
+ var type = plugin.utils.resolveClassName(classRef, compilationUnit);
+ var intermediaryName = mappings.resolveMethodToIntermediary(
+ type.tsym.flatName().toString(),
+ clearName
+ );
+ fieldAccess.name = plugin.names.fromString("id");
+ node.args = List.of(plugin.treeMaker.Literal(intermediaryName));
+ }
+
+ public void replaceClassName(JCTree.JCMethodInvocation node) {
+ var select = node.getMethodSelect();
+ if (!(select instanceof JCTree.JCFieldAccess fieldAccess)) return;
+ if (!fieldAccess.name.contentEquals("className")) return;
+ if (node.getTypeArguments().size() != 1) {
+ plugin.utils.reportError(sourceFile, node, "You need to explicitly provide the class you want the intermediary name for");
+ return;
+ }
+ var head = node.typeargs.head;
+ var resolved = plugin.utils.resolveClassName(head, compilationUnit);
+ var mappedName = mappings.resolveClassToIntermediary(resolved.tsym.flatName().toString());
+ fieldAccess.name = plugin.names.fromString("id");
+ node.typeargs = List.nil();
+ node.args = List.of(plugin.treeMaker.Literal(mappedName));
+ }
+
+ @Override
+ public Void visitMethodInvocation(MethodInvocationTree node, Void unused) {
+ replaceClassName((JCTree.JCMethodInvocation) node);
+ replaceMethodName((JCTree.JCMethodInvocation) node);
+ return super.visitMethodInvocation(node, unused);
+ }
+}
diff --git a/javaplugin/src/main/java/moe/nea/firmament/javaplugin/IntermediaryNameResolutionPlugin.java b/javaplugin/src/main/java/moe/nea/firmament/javaplugin/IntermediaryNameResolutionPlugin.java
new file mode 100644
index 0000000..ba6a0c5
--- /dev/null
+++ b/javaplugin/src/main/java/moe/nea/firmament/javaplugin/IntermediaryNameResolutionPlugin.java
@@ -0,0 +1,25 @@
+package moe.nea.firmament.javaplugin;
+
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.Plugin;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class IntermediaryNameResolutionPlugin implements Plugin {
+
+ @Override
+ public String getName() {
+ return "IntermediaryNameReplacement";
+ }
+
+ @Override
+ public void init(JavacTask task, String... args) {
+ Map<String, String> argMap = new HashMap<>();
+ for (String arg : args) {
+ String[] parts = arg.split("=", 2);
+ argMap.put(parts[0], parts.length == 2 ? parts[1] : "true");
+ }
+ task.addTaskListener(new IntermediaryNameResolutionTask(this, task, argMap));
+ }
+}
diff --git a/javaplugin/src/main/java/moe/nea/firmament/javaplugin/IntermediaryNameResolutionTask.java b/javaplugin/src/main/java/moe/nea/firmament/javaplugin/IntermediaryNameResolutionTask.java
new file mode 100644
index 0000000..86a5598
--- /dev/null
+++ b/javaplugin/src/main/java/moe/nea/firmament/javaplugin/IntermediaryNameResolutionTask.java
@@ -0,0 +1,44 @@
+package moe.nea.firmament.javaplugin;
+
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskListener;
+import com.sun.tools.javac.api.BasicJavacTask;
+import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.util.Names;
+import net.fabricmc.stitch.commands.tinyv2.TinyV2Reader;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+public class IntermediaryNameResolutionTask implements TaskListener {
+ TreeMaker treeMaker;
+ Names names;
+ MappingTree mappings;
+ Utils utils;
+
+ public IntermediaryNameResolutionTask(IntermediaryNameResolutionPlugin intermediaryNameResolutionPlugin, JavacTask task, Map<String, String> argMap) {
+ var context = ((BasicJavacTask) task).getContext();
+ var mappingFile = new File(argMap.get("mappingFile"));
+ System.err.println("Loading mappings from " + mappingFile);
+ try {
+ var tinyV2File = TinyV2Reader.read(mappingFile.toPath());
+ mappings = new MappingTree(tinyV2File, argMap.get("sourceNs"), argMap.getOrDefault("targetNs", "intermediary"));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ treeMaker = TreeMaker.instance(context);
+ names = Names.instance(context);
+ utils = Utils.instance(context);
+ }
+
+ @Override
+ public void finished(TaskEvent e) {
+ if (e.getKind() != TaskEvent.Kind.ENTER) return;
+ if (e.getCompilationUnit() == null || e.getSourceFile() == null) return;
+ e.getCompilationUnit().accept(new InitReplacer(mappings, this), null);
+ e.getCompilationUnit().accept(new IntermediaryMethodReplacer(mappings, this), null);
+ }
+
+}
diff --git a/javaplugin/src/main/java/moe/nea/firmament/javaplugin/MappingTree.java b/javaplugin/src/main/java/moe/nea/firmament/javaplugin/MappingTree.java
new file mode 100644
index 0000000..7a270b7
--- /dev/null
+++ b/javaplugin/src/main/java/moe/nea/firmament/javaplugin/MappingTree.java
@@ -0,0 +1,48 @@
+package moe.nea.firmament.javaplugin;
+
+import net.fabricmc.stitch.commands.tinyv2.TinyClass;
+import net.fabricmc.stitch.commands.tinyv2.TinyFile;
+import net.fabricmc.stitch.commands.tinyv2.TinyMethod;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class MappingTree {
+
+ private final Map<String, TinyClass> classLookup;
+ private final int targetIndex;
+ private final int sourceIndex;
+
+ public MappingTree(TinyFile tinyV2File, String sourceNamespace, String targetNamespace) {
+ sourceIndex = tinyV2File.getHeader().getNamespaces().indexOf(sourceNamespace);
+ if (sourceIndex < 0)
+ throw new RuntimeException("Could not find source namespace " + sourceNamespace + " in mappings file.");
+ this.classLookup = tinyV2File
+ .getClassEntries()
+ .stream()
+ .collect(Collectors.toMap(it -> it.getClassNames().get(sourceIndex), it -> it));
+ targetIndex = tinyV2File.getHeader().getNamespaces().indexOf(targetNamespace);
+ if (targetIndex < 0)
+ throw new RuntimeException("Could not find target namespace " + targetNamespace + " in mappings file.");
+ }
+
+ public String resolveMethodToIntermediary(String className, String methodName) {
+ var classData = classLookup.get(className.replace(".", "/"));
+ TinyMethod candidate = null;
+ for (TinyMethod method : classData.getMethods()) {
+ if (method.getMethodNames().get(sourceIndex).equals(methodName)) {
+ if (candidate != null) {
+ throw new RuntimeException("Found two candidates for method " + className + "." + methodName);
+ }
+ candidate = method;
+ }
+ }
+ return candidate.getMethodNames().get(targetIndex);
+ }
+
+ public String resolveClassToIntermediary(String className) {
+ return classLookup.get(className.replace(".", "/"))
+ .getClassNames().get(targetIndex)
+ .replace("/", ".");
+ }
+}
diff --git a/javaplugin/src/main/java/moe/nea/firmament/javaplugin/Utils.java b/javaplugin/src/main/java/moe/nea/firmament/javaplugin/Utils.java
new file mode 100644
index 0000000..d9008bd
--- /dev/null
+++ b/javaplugin/src/main/java/moe/nea/firmament/javaplugin/Utils.java
@@ -0,0 +1,121 @@
+package moe.nea.firmament.javaplugin;
+
+import com.sun.source.tree.AnnotationTree;
+import com.sun.source.tree.AssignmentTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.comp.Attr;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Types;
+import com.sun.tools.javac.comp.AttrContext;
+import com.sun.tools.javac.comp.Enter;
+import com.sun.tools.javac.comp.Env;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.JCDiagnostic;
+import com.sun.tools.javac.util.JavacMessages;
+import com.sun.tools.javac.util.Log;
+
+import javax.tools.JavaFileObject;
+import java.util.ListResourceBundle;
+
+public class Utils {
+ private static final Context.Key<Utils> KEY = new Context.Key<>();
+ private final Log log;
+ private final JCDiagnostic.Factory diagnostics;
+ private final Types types;
+ private final Attr attr;
+ private final Enter enter;
+
+ private Utils(Context context) {
+ context.put(KEY, this);
+ JavacMessages.instance(context).add(l -> new ListResourceBundle() {
+
+ @Override
+ protected Object[][] getContents() {
+ return new Object[][]{
+ new Object[]{"compiler.err.firmament.generic", "{0}"}
+ };
+ }
+ });
+ log = Log.instance(context);
+ diagnostics = JCDiagnostic.Factory.instance(context);
+ types = Types.instance(context);
+ attr = Attr.instance(context);
+ enter = Enter.instance(context);
+ }
+
+ public static Utils instance(Context context) {
+ var utils = context.get(KEY);
+ if (utils == null) {
+ utils = new Utils(context);
+ }
+ return utils;
+ }
+
+ public Type resolveClassName(ExpressionTree expression) {
+ var tree = (JCTree) expression;
+ return tree.type;
+ }
+
+ public Type resolveClassName(ExpressionTree tree, CompilationUnitTree unit) {
+ return resolveClassName(tree, enter.getTopLevelEnv((JCTree.JCCompilationUnit) unit));
+ }
+
+ public Type resolveClassName(ExpressionTree tree, Env<AttrContext> env) {
+ var t = resolveClassName(tree);
+ if (t != null) return t;
+ return attr.attribType((JCTree) tree, env);
+ }
+
+ public Symbol getSymbol(IdentifierTree tree) {
+ return ((JCTree.JCIdent) tree).sym;
+ }
+
+ public Symbol.ClassSymbol getSymbol(ClassTree tree) {
+ return ((JCTree.JCClassDecl) tree).sym;
+ }
+
+ public ExpressionTree getAnnotationValue(
+ AnnotationTree tree,
+ String name) {
+ // TODO: strip parenthesis
+ for (var argument : tree.getArguments()) {
+ var assignment = (AssignmentTree) argument;
+ if (((IdentifierTree) assignment.getVariable()).getName().toString().equals(name))
+ return assignment.getExpression();
+ }
+ return null;
+ }
+
+ public Type.ClassType resolveClassLiteralExpression(ExpressionTree tree) {
+ if (!(tree instanceof MemberSelectTree select))
+ throw new RuntimeException("Cannot resolve non field access class literal: " + tree);
+ if (!select.getIdentifier().toString().equals("class"))
+ throw new RuntimeException("Class literal " + select + "accessed non .class attribute");
+
+ return (Type.ClassType) resolveClassName(select.getExpression());
+ }
+
+ public void reportError(
+ JavaFileObject file,
+ Tree node,
+ String message
+ ) {
+ var originalSource = log.useSource(file);
+ var error = diagnostics.error(
+ JCDiagnostic.DiagnosticFlag.API,
+ log.currentSource(),
+ ((JCTree) node).pos(),
+ "firmament.generic",
+ message
+ );
+ log.report(error);
+ log.useSource(originalSource);
+ }
+}
diff --git a/javaplugin/src/main/resources/META-INF/services/com.sun.source.util.Plugin b/javaplugin/src/main/resources/META-INF/services/com.sun.source.util.Plugin
new file mode 100644
index 0000000..a9e5dbe
--- /dev/null
+++ b/javaplugin/src/main/resources/META-INF/services/com.sun.source.util.Plugin
@@ -0,0 +1 @@
+moe.nea.firmament.javaplugin.IntermediaryNameResolutionPlugin
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 79cf1a4..36e7e3e 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -33,3 +33,4 @@ pluginManagement {
rootProject.name = "Firmament"
include("symbols")
+include("javaplugin")
diff --git a/src/main/java/moe/nea/firmament/init/ClientPlayerRiser.java b/src/main/java/moe/nea/firmament/init/ClientPlayerRiser.java
index 18dfa17..0a5bedd 100644
--- a/src/main/java/moe/nea/firmament/init/ClientPlayerRiser.java
+++ b/src/main/java/moe/nea/firmament/init/ClientPlayerRiser.java
@@ -1,4 +1,3 @@
-
package moe.nea.firmament.init;
import me.shedaniel.mm.api.ClassTinkerers;
@@ -14,11 +13,15 @@ import java.lang.reflect.Modifier;
import java.util.Objects;
public class ClientPlayerRiser extends RiserUtils {
- String PlayerEntity = remapper.mapClassName("intermediary", "net.minecraft.class_1657");
- String World = remapper.mapClassName("intermediary", "net.minecraft.class_1937");
+ @IntermediaryName(net.minecraft.entity.player.PlayerEntity.class)
+ String PlayerEntity;
+ @IntermediaryName(net.minecraft.world.World.class)
+ String World;
String GameProfile = "com.mojang.authlib.GameProfile";
- String BlockPos = remapper.mapClassName("intermediary", "net.minecraft.class_2338");
- String AbstractClientPlayerEntity = remapper.mapClassName("intermediary", "net.minecraft.class_742");
+ @IntermediaryName(net.minecraft.util.math.BlockPos.class)
+ String BlockPos;
+ @IntermediaryName(net.minecraft.client.network.AbstractClientPlayerEntity.class)
+ String AbstractClientPlayerEntity;
String GuiPlayer = "moe.nea.firmament.gui.entity.GuiPlayer";
// World world, BlockPos pos, float yaw, GameProfile gameProfile
Type constructorDescriptor = Type.getMethodType(Type.VOID_TYPE, getTypeForClassName(World), getTypeForClassName(BlockPos), Type.FLOAT_TYPE, getTypeForClassName(GameProfile));
diff --git a/src/main/java/moe/nea/firmament/init/EarlyRiser.java b/src/main/java/moe/nea/firmament/init/EarlyRiser.java
index 77c044d..5eab563 100644
--- a/src/main/java/moe/nea/firmament/init/EarlyRiser.java
+++ b/src/main/java/moe/nea/firmament/init/EarlyRiser.java
@@ -6,5 +6,6 @@ public class EarlyRiser implements Runnable {
public void run() {
new ClientPlayerRiser().addTinkerers();
new HandledScreenRiser().addTinkerers();
+ new SectionBuilderRiser().addTinkerers();
}
}
diff --git a/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java b/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java
index 0215523..baa0501 100644
--- a/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java
+++ b/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java
@@ -2,6 +2,8 @@
package moe.nea.firmament.init;
import me.shedaniel.mm.api.ClassTinkerers;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.screen.ingame.HandledScreen;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
@@ -17,8 +19,10 @@ import org.objectweb.asm.tree.VarInsnNode;
import java.lang.reflect.Modifier;
public class HandledScreenRiser extends RiserUtils {
- String Screen = remapper.mapClassName("intermediary", "net.minecraft.class_437");
- String HandledScreen = remapper.mapClassName("intermediary", "net.minecraft.class_465");
+ @IntermediaryName(net.minecraft.client.gui.screen.Screen.class)
+ String Screen;
+ @IntermediaryName(net.minecraft.client.gui.screen.ingame.HandledScreen.class)
+ String HandledScreen;
Type mouseScrolledDesc = Type.getMethodType(Type.BOOLEAN_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE);
String mouseScrolled = remapper.mapMethodName("intermediary", "net.minecraft.class_364", "method_25401",
mouseScrolledDesc.getDescriptor());
diff --git a/src/main/java/moe/nea/firmament/init/Intermediary.java b/src/main/java/moe/nea/firmament/init/Intermediary.java
new file mode 100644
index 0000000..61494d7
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/init/Intermediary.java
@@ -0,0 +1,63 @@
+package moe.nea.firmament.init;
+
+import net.fabricmc.loader.api.FabricLoader;
+import net.fabricmc.loader.api.MappingResolver;
+import org.objectweb.asm.Type;
+
+import java.util.List;
+
+public class Intermediary {
+ private static final MappingResolver RESOLVER = FabricLoader.getInstance().getMappingResolver();
+
+ static String methodName(Object object) {
+ throw new AssertionError("Cannot be called at runtime");
+ }
+
+ static <T> String className() {
+ throw new AssertionError("Cannot be called at runtime");
+ }
+
+ static String id(String source) {
+ return source;
+ }
+
+// public record Class(
+// Type intermediaryClass
+// ) {
+// public Class(String intermediaryClass) {
+// this(Type.getObjectType(intermediaryClass.replace('.', '/')));
+// }
+//
+// public String getMappedName() {
+// return RESOLVER.mapClassName("intermediary", intermediaryClass.getInternalName()
+// .replace('/', '.'));
+// }
+// }
+//
+// public record Method(
+// Type intermediaryClassName,
+// String intermediaryMethodName,
+// Type intermediaryReturnType,
+// List<Type> intermediaryArgumentTypes
+// ) {
+// public Method(
+// String intermediaryClassName,
+// String intermediaryMethodName,
+// String intermediaryReturnType,
+// String... intermediaryArgumentTypes
+// ) {
+// this(intermediaryClassName, intermediaryMethodName, intermediaryReturnType, List.of(intermediaryArgumentTypes));
+// }
+//
+// public String getMappedMethodName() {
+// return RESOLVER.mapMethodName("intermediary",
+// intermediaryClassName.getInternalName().replace('/', '.'));
+// }
+//
+// public Type getIntermediaryDescriptor() {
+// return Type.getMethodType(intermediaryReturnType, intermediaryArgumentTypes.toArray(Type[]::new));
+// }
+//
+//
+// }
+}
diff --git a/src/main/java/moe/nea/firmament/init/IntermediaryName.java b/src/main/java/moe/nea/firmament/init/IntermediaryName.java
new file mode 100644
index 0000000..a22ad0f
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/init/IntermediaryName.java
@@ -0,0 +1,21 @@
+package moe.nea.firmament.init;
+
+import net.fabricmc.loader.api.MappingResolver;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Injects the intermediary name of the given field into this field by replacing its initializer with a call to
+ * {@link MappingResolver#mapClassName(String, String)}
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.FIELD)
+public @interface IntermediaryName {
+ // String method() default "";
+//
+// String field() default "";
+ Class<?> value();
+}
diff --git a/src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java b/src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java
new file mode 100644
index 0000000..2be11a6
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java
@@ -0,0 +1,116 @@
+package moe.nea.firmament.init;
+
+import me.shedaniel.mm.api.ClassTinkerers;
+import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.block.BlockState;
+import net.minecraft.client.render.block.BlockModels;
+import net.minecraft.client.render.block.BlockRenderManager;
+import net.minecraft.client.render.chunk.SectionBuilder;
+import net.minecraft.client.render.model.BakedModel;
+import net.minecraft.util.math.BlockPos;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.LocalVariableNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.VarInsnNode;
+
+public class SectionBuilderRiser extends RiserUtils {
+
+ @IntermediaryName(SectionBuilder.class)
+ String SectionBuilder;
+ @IntermediaryName(BlockPos.class)
+ String BlockPos;
+ @IntermediaryName(BlockRenderManager.class)
+ String BlockRenderManager;
+ @IntermediaryName(BlockState.class)
+ String BlockState;
+ @IntermediaryName(BakedModel.class)
+ String BakedModel;
+ String CustomBlockTextures = "moe.nea.firmament.features.texturepack.CustomBlockTextures";
+
+ Type getModelDesc = Type.getMethodType(
+ getTypeForClassName(BlockRenderManager),
+ getTypeForClassName(BlockState)
+ );
+ String getModel = remapper.mapMethodName(
+ "intermediary",
+ Intermediary.<BlockRenderManager>className(),
+ Intermediary.methodName(net.minecraft.client.render.block.BlockRenderManager::getModel),
+ Type.getMethodDescriptor(
+ getTypeForClassName(Intermediary.<BakedModel>className()),
+ getTypeForClassName(Intermediary.<BlockState>className())
+ )
+ );
+
+ @Override
+ public void addTinkerers() {
+ if (FabricLoader.getInstance().isModLoaded("fabric-renderer-indigo"))
+ ClassTinkerers.addTransformation(SectionBuilder, this::handle, true);
+ }
+
+ private void handle(ClassNode classNode) {
+ for (MethodNode method : classNode.methods) {
+ if (method.name.endsWith("$fabric-renderer-indigo$hookChunkBuildTessellate") &&
+ method.name.startsWith("redirect$")) {
+ handleIndigo(method);
+ return;
+ }
+ }
+ new RuntimeException("Could not inject tesselation hook despite fabric renderer indigo being loaded").printStackTrace();
+ }
+
+ private void handleIndigo(MethodNode method) {
+ LocalVariableNode blockPosVar = null, blockStateVar = null;
+ for (LocalVariableNode localVariable : method.localVariables) {
+ if (Type.getType(localVariable.desc).equals(getTypeForClassName(BlockPos))) {
+ blockPosVar = localVariable;
+ }
+ if (Type.getType(localVariable.desc).equals(getTypeForClassName(BlockState))) {
+ blockStateVar = localVariable;
+ }
+ }
+ if (blockPosVar == null || blockStateVar == null) {
+ System.err.println("Firmament could inject into indigo: missing either block pos or blockstate");
+ return;
+ }
+ for (AbstractInsnNode instruction : method.instructions) {
+ if (instruction.getOpcode() != Opcodes.INVOKEVIRTUAL) continue;
+ var methodInsn = (MethodInsnNode) instruction;
+ if (!(methodInsn.name.equals(getModel) && Type.getObjectType(methodInsn.owner).equals(getTypeForClassName(BlockRenderManager))))
+ continue;
+ method.instructions.insertBefore(
+ methodInsn,
+ new MethodInsnNode(
+ Opcodes.INVOKESTATIC,
+ getTypeForClassName(CustomBlockTextures).getInternalName(),
+ "enterFallbackCall",
+ Type.getMethodDescriptor(Type.VOID_TYPE)
+ ));
+
+ var insnList = new InsnList();
+ insnList.add(new MethodInsnNode(
+ Opcodes.INVOKESTATIC,
+ getTypeForClassName(CustomBlockTextures).getInternalName(),
+ "exitFallbackCall",
+ Type.getMethodDescriptor(Type.VOID_TYPE)
+ ));
+ insnList.add(new VarInsnNode(Opcodes.ALOAD, blockPosVar.index));
+ insnList.add(new VarInsnNode(Opcodes.ALOAD, blockStateVar.index));
+ insnList.add(new MethodInsnNode(
+ Opcodes.INVOKESTATIC,
+ getTypeForClassName(CustomBlockTextures).getInternalName(),
+ "patchIndigo",
+ Type.getMethodDescriptor(getTypeForClassName(BakedModel),
+ getTypeForClassName(BakedModel),
+ getTypeForClassName(BlockPos),
+ getTypeForClassName(BlockState)),
+ false
+ ));
+ method.instructions.insert(methodInsn, insnList);
+ }
+ }
+}
diff --git a/src/main/kotlin/moe/nea/firmament/Firmament.kt b/src/main/kotlin/moe/nea/firmament/Firmament.kt
index 4b742e8..400dcf2 100644
--- a/src/main/kotlin/moe/nea/firmament/Firmament.kt
+++ b/src/main/kotlin/moe/nea/firmament/Firmament.kt
@@ -33,6 +33,7 @@ import kotlinx.coroutines.plus
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import kotlin.coroutines.EmptyCoroutineContext
+import net.minecraft.client.render.chunk.SectionBuilder
import net.minecraft.command.CommandRegistryAccess
import net.minecraft.util.Identifier
import moe.nea.firmament.commands.registerFirmamentCommand
@@ -112,6 +113,8 @@ object Firmament {
ClientTickEvents.END_CLIENT_TICK.register(ClientTickEvents.EndTick { instance ->
TickEvent.publish(TickEvent(tick++))
})
+ // TODO: remove me
+ Class.forName(SectionBuilder::class.java.name)
IDataHolder.registerEvents()
RepoManager.initialize()
SBData.init()
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt
index 2289be2..c869ba4 100644
--- a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt
@@ -157,7 +157,10 @@ object CustomBlockTextures {
currentIslandReplacements = replacements
if (lastReplacements != replacements) {
MC.nextTick {
- MC.worldRenderer.reload()
+ MC.worldRenderer.chunks?.chunks?.forEach {
+ // false schedules rebuilds outside a 27 block radius to happen async
+ it.scheduleRebuild(false)
+ }
}
}
}
@@ -259,6 +262,10 @@ object CustomBlockTextures {
return BakedReplacements(map.mapValues { LocationReplacements(it.value) })
}
+ @JvmStatic
+ fun patchIndigo(orig: BakedModel, pos: BlockPos, state: BlockState): BakedModel {
+ return getReplacementModel(state, pos) ?: orig
+ }
@Subscribe
fun onStart(event: FinalizeResourceManagerEvent) {
diff --git a/src/main/resources/firmament.accesswidener b/src/main/resources/firmament.accesswidener
index b69b99d..c69725f 100644
--- a/src/main/resources/firmament.accesswidener
+++ b/src/main/resources/firmament.accesswidener
@@ -20,4 +20,4 @@ mutable field net/minecraft/screen/slot/Slot x I
mutable field net/minecraft/screen/slot/Slot y I
accessible field net/minecraft/entity/player/PlayerEntity PLAYER_MODEL_PARTS Lnet/minecraft/entity/data/TrackedData;
-
+accessible field net/minecraft/client/render/WorldRenderer chunks Lnet/minecraft/client/render/BuiltChunkStorage;