WIP: Port to compilation on 1.21.4
This commit is contained in:
@@ -25,7 +25,7 @@ plugins {
|
||||
alias(libs.plugins.kotlin.plugin.ksp)
|
||||
// alias(libs.plugins.loom)
|
||||
// TODO: use arch loom once they update to 1.8
|
||||
id("fabric-loom") version "1.8.9"
|
||||
id("fabric-loom") version "1.9.2"
|
||||
alias(libs.plugins.shadow)
|
||||
id("moe.nea.licenseextractificator")
|
||||
id("moe.nea.mc-auto-translations") version "0.1.0"
|
||||
@@ -129,13 +129,8 @@ val collectTranslations by tasks.registering(CollectTranslations::class) {
|
||||
val compatSourceSets: MutableSet<SourceSet> = mutableSetOf()
|
||||
fun createIsolatedSourceSet(name: String, path: String = "compat/$name", isEnabled: Boolean = true): SourceSet {
|
||||
val ss = sourceSets.create(name) {
|
||||
if (isEnabled) {
|
||||
this.java.setSrcDirs(listOf(layout.projectDirectory.dir("src/$path/java")))
|
||||
this.kotlin.setSrcDirs(listOf(layout.projectDirectory.dir("src/$path/java")))
|
||||
} else {
|
||||
this.java.setSrcDirs(listOf<File>())
|
||||
this.kotlin.setSrcDirs(listOf<File>())
|
||||
}
|
||||
this.java.setSrcDirs(listOf(layout.projectDirectory.dir("src/$path/java")))
|
||||
this.kotlin.setSrcDirs(listOf(layout.projectDirectory.dir("src/$path/java")))
|
||||
}
|
||||
val mainSS = sourceSets.main.get()
|
||||
val upperName = ss.name.capitalizeN()
|
||||
@@ -221,15 +216,16 @@ val testAgent by configurations.creating {
|
||||
|
||||
val configuredSourceSet = createIsolatedSourceSet("configured",
|
||||
isEnabled = false) // Wait for update (also low prio, because configured sucks)
|
||||
val sodiumSourceSet = createIsolatedSourceSet("sodium")
|
||||
val sodiumSourceSet = createIsolatedSourceSet("sodium", isEnabled = false)
|
||||
val citResewnSourceSet = createIsolatedSourceSet("citresewn", isEnabled = false) // TODO: Wait for update
|
||||
val yaclSourceSet = createIsolatedSourceSet("yacl")
|
||||
val yaclSourceSet = createIsolatedSourceSet("yacl", isEnabled = false)
|
||||
val explosiveEnhancementSourceSet =
|
||||
createIsolatedSourceSet("explosiveEnhancement", isEnabled = false) // TODO: wait for their port
|
||||
val wildfireGenderSourceSet = createIsolatedSourceSet("wildfireGender", isEnabled = false) // TODO: wait on their port
|
||||
val modmenuSourceSet = createIsolatedSourceSet("modmenu")
|
||||
val reiSourceSet = createIsolatedSourceSet("rei")
|
||||
val moulconfigSourceSet = createIsolatedSourceSet("moulconfig")
|
||||
val modmenuSourceSet = createIsolatedSourceSet("modmenu", isEnabled = false)
|
||||
val reiSourceSet = createIsolatedSourceSet("rei", isEnabled = false)
|
||||
val moulconfigSourceSet = createIsolatedSourceSet("moulconfig", isEnabled = false)
|
||||
val customTexturesSourceSet = createIsolatedSourceSet("texturePacks", "texturePacks")
|
||||
|
||||
dependencies {
|
||||
// Minecraft dependencies
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "1.8.10"
|
||||
kotlin("jvm") version "2.1.0"
|
||||
`kotlin-dsl`
|
||||
}
|
||||
repositories {
|
||||
|
||||
@@ -3,21 +3,23 @@
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
[versions]
|
||||
minecraft = "1.21.3"
|
||||
minecraft = "1.21.4"
|
||||
|
||||
# Update from https://kotlinlang.org/
|
||||
kotlin = "2.0.21"
|
||||
kotlin = "2.1.0"
|
||||
# Update from https://github.com/google/ksp/releases
|
||||
kotlin_ksp = "2.0.21-1.0.26"
|
||||
kotlin_ksp = "2.1.0-1.0.29"
|
||||
|
||||
# Update from https://linkie.shedaniel.me/dependencies?loader=fabric
|
||||
fabric_loader = "0.16.9"
|
||||
fabric_api = "0.107.0+1.21.3"
|
||||
fabric_kotlin = "1.12.3+kotlin.2.0.21"
|
||||
yarn = "1.21.3+build.2"
|
||||
fabric_api = "0.110.5+1.21.4"
|
||||
yarn = "1.21.4+build.1"
|
||||
modmenu = "13.0.0-beta.1"
|
||||
architectury = "15.0.1"
|
||||
rei = "17.0.789"
|
||||
modmenu = "12.0.0-beta.1"
|
||||
architectury = "14.0.4"
|
||||
|
||||
# Update from https://maven.fabricmc.net/net/fabricmc/fabric-language-kotlin/
|
||||
fabric_kotlin = "1.13.0+kotlin.2.1.0"
|
||||
|
||||
# Update from https://maven.architectury.dev/dev/architectury/loom/dev.architectury.loom.gradle.plugin/
|
||||
loom = "1.7.414"
|
||||
@@ -26,16 +28,16 @@ loom = "1.7.414"
|
||||
qolify = "1.6.0-1.21.1"
|
||||
|
||||
# Update from https://modrinth.com/mod/sodium/versions?l=fabric
|
||||
sodium = "mc1.21.3-0.6.0-beta.4-fabric"
|
||||
sodium = "mc1.21.4-0.6.2-fabric"
|
||||
|
||||
# Update from https://modrinth.com/mod/freecam/versions?l=fabric
|
||||
freecammod = "vomskVK3"
|
||||
freecammod = "1.3.1+mc1.21.3"
|
||||
|
||||
# Update from https://modrinth.com/mod/no-chat-reports/versions?l=fabric
|
||||
ncr = "Fabric-1.21.3-v2.10.0"
|
||||
|
||||
# Update from https://modrinth.com/mod/female-gender/versions?l=fabric
|
||||
femalegender = "kJmjQvAS"
|
||||
femalegender = "4.3+1.21.4"
|
||||
|
||||
# Update from https://modrinth.com/mod/explosive-enhancement/versions?l=fabric
|
||||
explosiveenhancement = "1.2.3-1.21.0"
|
||||
@@ -49,12 +51,13 @@ citresewn = "1.2.0+1.21"
|
||||
devauth = "1.2.0"
|
||||
|
||||
# Update from https://ktor.io/
|
||||
ktor = "3.0.1"
|
||||
ktor = "3.0.2"
|
||||
|
||||
# Update from https://repo.nea.moe/#/releases/moe/nea/neurepoparser
|
||||
neurepoparser = "1.6.0"
|
||||
|
||||
# Update from https://github.com/HotswapProjects/HotswapAgent/releases
|
||||
# TODO: bump to 2.0.1
|
||||
hotswap_agent = "1.4.2-SNAPSHOT"
|
||||
|
||||
# Update from https://github.com/LlamaLad7/MixinExtras/tags
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -4,6 +4,6 @@
|
||||
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -59,6 +59,14 @@ public class InitReplacer extends TreeScanner<Void, Void> {
|
||||
var target = plugin.utils.getAnnotationValue(jcAnnotation, "value");
|
||||
var targetClass = plugin.utils.resolveClassLiteralExpression(target).tsym.flatName().toString();
|
||||
var intermediaryClass = mappingTree.resolveClassToIntermediary(targetClass);
|
||||
if (intermediaryClass == null){
|
||||
plugin.utils.reportError(
|
||||
compilationUnitTree.getSourceFile(),
|
||||
jcNode.init,
|
||||
"Unknown class name " + targetClass
|
||||
);
|
||||
return super.visitVariable(node, unused);
|
||||
}
|
||||
var remapper = treeMaker.Select(treeMaker.This(classTree.type), names.fromString("remapper"));
|
||||
var remappingCall = treeMaker.Apply(
|
||||
List.nil(),
|
||||
|
||||
@@ -57,7 +57,12 @@ public class IntermediaryMethodReplacer extends TreeScanner<Void, Void> {
|
||||
}
|
||||
var head = node.typeargs.head;
|
||||
var resolved = plugin.utils.resolveClassName(head, compilationUnit);
|
||||
var mappedName = mappings.resolveClassToIntermediary(resolved.tsym.flatName().toString());
|
||||
var sourceName = resolved.tsym.flatName().toString();
|
||||
var mappedName = mappings.resolveClassToIntermediary(sourceName);
|
||||
if (mappedName == null) {
|
||||
plugin.utils.reportError(sourceFile, node, "Unknown class name " + sourceName);
|
||||
return;
|
||||
}
|
||||
fieldAccess.name = plugin.names.fromString("id");
|
||||
node.typeargs = List.nil();
|
||||
node.args = List.of(plugin.treeMaker.Literal(mappedName));
|
||||
|
||||
@@ -9,40 +9,43 @@ import java.util.stream.Collectors;
|
||||
|
||||
public class MappingTree {
|
||||
|
||||
private final Map<String, TinyClass> classLookup;
|
||||
private final int targetIndex;
|
||||
private final int sourceIndex;
|
||||
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 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 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("/", ".");
|
||||
}
|
||||
public String resolveClassToIntermediary(String className) {
|
||||
var cls = classLookup.get(className.replace(".", "/"));
|
||||
if (cls == null) {
|
||||
return null;
|
||||
}
|
||||
return cls.getClassNames().get(targetIndex)
|
||||
.replace("/", ".");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ public class Utils {
|
||||
var error = diagnostics.error(
|
||||
JCDiagnostic.DiagnosticFlag.API,
|
||||
log.currentSource(),
|
||||
((JCTree) node).pos(),
|
||||
node == null ? null : ((JCTree) node).pos(),
|
||||
"firmament.generic",
|
||||
message
|
||||
);
|
||||
|
||||
@@ -7,6 +7,6 @@ public class EarlyRiser implements Runnable {
|
||||
new ClientPlayerRiser().addTinkerers();
|
||||
new HandledScreenRiser().addTinkerers();
|
||||
new SectionBuilderRiser().addTinkerers();
|
||||
new ItemColorsSodiumRiser().addTinkerers();
|
||||
// TODO: new ItemColorsSodiumRiser().addTinkerers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package moe.nea.firmament.init;
|
||||
|
||||
import me.shedaniel.mm.api.ClassTinkerers;
|
||||
import net.minecraft.client.gui.Element;
|
||||
import net.minecraft.client.gui.ParentElement;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package moe.nea.firmament.init;
|
||||
|
||||
import me.shedaniel.mm.api.ClassTinkerers;
|
||||
import moe.nea.firmament.util.ErrorUtil;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.color.item.ItemColorProvider;
|
||||
import net.minecraft.client.color.item.ItemColors;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.InsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.VarInsnNode;
|
||||
|
||||
public class ItemColorsSodiumRiser extends RiserUtils {
|
||||
@IntermediaryName(ItemColors.class)
|
||||
String ItemColors;
|
||||
@IntermediaryName(ItemColorProvider.class)
|
||||
String ItemColorProvider;
|
||||
@IntermediaryName(ItemStack.class)
|
||||
String ItemStack;
|
||||
String getColorProvider = "sodium$getColorProvider";
|
||||
Type getColorProviderDesc = Type.getMethodType(getTypeForClassName(ItemColorProvider),
|
||||
getTypeForClassName(ItemStack));
|
||||
|
||||
@Override
|
||||
public void addTinkerers() {
|
||||
ClassTinkerers.addTransformation(ItemColors, this::addSodiumOverride, true);
|
||||
}
|
||||
|
||||
private void addSodiumOverride(ClassNode classNode) {
|
||||
var node = findMethod(classNode, getColorProvider, getColorProviderDesc);
|
||||
if (node == null) {
|
||||
if (!FabricLoader.getInstance().isModLoaded("sodium"))
|
||||
ErrorUtil.INSTANCE.softError("Sodium is present, but sodium color override could not be injected.");
|
||||
return;
|
||||
}
|
||||
var p = node.instructions.getFirst();
|
||||
while (p != null) {
|
||||
if (p.getOpcode() == Opcodes.ARETURN) {
|
||||
node.instructions.insertBefore(
|
||||
p,
|
||||
mkOverrideSodiumCall()
|
||||
);
|
||||
}
|
||||
p = p.getNext();
|
||||
}
|
||||
}
|
||||
|
||||
private InsnList mkOverrideSodiumCall() {
|
||||
var insnList = new InsnList();
|
||||
insnList.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
insnList.add(new InsnNode(Opcodes.SWAP));
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
|
||||
getTypeForClassName(ItemColors).getInternalName(),
|
||||
"overrideSodium_firmament",
|
||||
Type.getMethodType(getTypeForClassName(ItemColorProvider),
|
||||
getTypeForClassName(ItemColorProvider)).getDescriptor(),
|
||||
false));
|
||||
return insnList;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.mixins;
|
||||
|
||||
import moe.nea.firmament.events.CustomItemModelEvent;
|
||||
import moe.nea.firmament.features.texturepack.CustomGlobalTextures;
|
||||
import net.minecraft.client.render.item.ItemModels;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.BakedModelManager;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ItemModels.class)
|
||||
public class CustomModelEventPatch {
|
||||
|
||||
@Inject(method = "getModel(Lnet/minecraft/item/ItemStack;)Lnet/minecraft/client/render/model/BakedModel;", at = @At("HEAD"), cancellable = true)
|
||||
public void onGetModel(ItemStack stack, CallbackInfoReturnable<BakedModel> cir) {
|
||||
var $this = (ItemModels) (Object) this;
|
||||
var model = CustomItemModelEvent.getModel(stack, $this);
|
||||
if (model == null) {
|
||||
model = CustomGlobalTextures.replaceGlobalModel($this, stack);
|
||||
}
|
||||
if (model != null) {
|
||||
cir.setReturnValue(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
@Mixin(ClientPlayNetworkHandler.class)
|
||||
public abstract class IncomingPacketListenerPatches {
|
||||
|
||||
|
||||
@ModifyExpressionValue(method = "onCommandTree", at = @At(value = "NEW", target = "(Lcom/mojang/brigadier/tree/RootCommandNode;)Lcom/mojang/brigadier/CommandDispatcher;", remap = false))
|
||||
public CommandDispatcher onOnCommandTree(CommandDispatcher dispatcher) {
|
||||
MaskCommands.Companion.publish(new MaskCommands(dispatcher));
|
||||
@@ -31,7 +30,7 @@ public abstract class IncomingPacketListenerPatches {
|
||||
packet.getParameters(),
|
||||
new Vec3d(packet.getX(), packet.getY(), packet.getZ()),
|
||||
new Vector3f(packet.getOffsetX(), packet.getOffsetY(), packet.getOffsetZ()),
|
||||
packet.isLongDistance(),
|
||||
packet.isImportant(),
|
||||
packet.getCount(),
|
||||
packet.getSpeed()
|
||||
);
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
|
||||
import moe.nea.firmament.features.texturepack.BakedModelExtra;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.item.ItemRenderer;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ModelTransformationMode;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ItemRenderer.class)
|
||||
public class ApplyHeadModelInItemRenderer {
|
||||
@Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;ZF)V",
|
||||
at = @At("HEAD"))
|
||||
private void applyHeadModel(ItemStack stack, ModelTransformationMode transformationMode, boolean leftHanded,
|
||||
MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay,
|
||||
BakedModel model, boolean useInventoryModel, float z, CallbackInfo ci,
|
||||
@Local(argsOnly = true) LocalRef<BakedModel> modelMut
|
||||
) {
|
||||
var extra = BakedModelExtra.cast(model);
|
||||
if (transformationMode == ModelTransformationMode.HEAD && extra != null) {
|
||||
var headModel = extra.getHeadModel_firmament();
|
||||
if (headModel != null) {
|
||||
modelMut.set(headModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import moe.nea.firmament.features.texturepack.BakedModelExtra;
|
||||
import moe.nea.firmament.features.texturepack.TintOverrides;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.BasicBakedModel;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(BasicBakedModel.class)
|
||||
public class BakedModelDataHolderBasic implements BakedModelExtra {
|
||||
|
||||
@Unique
|
||||
private BakedModel headModel;
|
||||
|
||||
@Unique
|
||||
@Nullable
|
||||
private TintOverrides tintOverrides;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BakedModel getHeadModel_firmament() {
|
||||
return headModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeadModel_firmament(@Nullable BakedModel headModel) {
|
||||
this.headModel = headModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TintOverrides getTintOverrides_firmament() {
|
||||
return tintOverrides;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTintOverrides_firmament(@Nullable TintOverrides tintOverrides) {
|
||||
this.tintOverrides = tintOverrides;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import moe.nea.firmament.features.texturepack.BakedModelExtra;
|
||||
import moe.nea.firmament.features.texturepack.TintOverrides;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.BuiltinBakedModel;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(BuiltinBakedModel.class)
|
||||
public class BakedModelDataHolderBuiltin implements BakedModelExtra {
|
||||
|
||||
@Unique
|
||||
@Nullable
|
||||
private BakedModel headModel;
|
||||
|
||||
@Unique
|
||||
@Nullable
|
||||
private TintOverrides tintOverrides;
|
||||
|
||||
@Override
|
||||
public @Nullable TintOverrides getTintOverrides_firmament() {
|
||||
return tintOverrides;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTintOverrides_firmament(@Nullable TintOverrides tintOverrides) {
|
||||
this.tintOverrides = tintOverrides;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BakedModel getHeadModel_firmament() {
|
||||
return headModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeadModel_firmament(@Nullable BakedModel headModel) {
|
||||
this.headModel = headModel;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import moe.nea.firmament.features.texturepack.BakedOverrideData;
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate;
|
||||
import net.minecraft.client.render.model.json.ModelOverrideList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(ModelOverrideList.BakedOverride.class)
|
||||
public class BakedOverrideDataHolder implements BakedOverrideData {
|
||||
|
||||
@Unique
|
||||
private FirmamentModelPredicate[] firmamentOverrides;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FirmamentModelPredicate[] getFirmamentOverrides() {
|
||||
return firmamentOverrides;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFirmamentOverrides(@NotNull FirmamentModelPredicate[] overrides) {
|
||||
this.firmamentOverrides = overrides;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.features.texturepack.BakedModelExtra;
|
||||
import net.minecraft.block.AbstractSkullBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.entity.LivingEntityRenderer;
|
||||
import net.minecraft.client.render.entity.feature.HeadFeatureRenderer;
|
||||
import net.minecraft.client.render.entity.model.EntityModel;
|
||||
import net.minecraft.client.render.entity.model.ModelWithHead;
|
||||
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.EquipmentSlot;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(HeadFeatureRenderer.class)
|
||||
public class HeadModelReplacerPatch<S extends LivingEntityRenderState, M extends EntityModel<S> & ModelWithHead> {
|
||||
/**
|
||||
* This class serves to disable the replacing of head models with the vanilla block model. Vanilla first selects loads
|
||||
* the model containing the head model regularly in {@link LivingEntityRenderer#updateRenderState}, but then discards
|
||||
* the model in {@link HeadFeatureRenderer#render(MatrixStack, VertexConsumerProvider, int, LivingEntityRenderState, float, float)}
|
||||
* if it detects a skull block. This serves to disable that functionality if a head model override is present.
|
||||
*/
|
||||
@WrapOperation(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/LivingEntityRenderState;FF)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/item/BlockItem;getBlock()Lnet/minecraft/block/Block;"))
|
||||
private Block replaceSkull(BlockItem instance, Operation<Block> original, @Local BakedModel bakedModel) {
|
||||
var oldBlock = original.call(instance);
|
||||
if (oldBlock instanceof AbstractSkullBlock) {
|
||||
var extra = BakedModelExtra.cast(bakedModel);
|
||||
if (extra != null && extra.getHeadModel_firmament() != null)
|
||||
return Blocks.ENCHANTING_TABLE; // Any non skull block. Let's choose the enchanting table because it is very distinct.
|
||||
}
|
||||
return oldBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* We disable the has model override, since texture packs get precedent to server data.
|
||||
*/
|
||||
@WrapOperation(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/LivingEntityRenderState;FF)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/feature/ArmorFeatureRenderer;hasModel(Lnet/minecraft/item/ItemStack;Lnet/minecraft/entity/EquipmentSlot;)Z"))
|
||||
private boolean replaceHasModel(ItemStack stack, EquipmentSlot slot, Operation<Boolean> original,
|
||||
@Local BakedModel bakedModel) {
|
||||
var extra = BakedModelExtra.cast(bakedModel);
|
||||
if (extra != null && extra.getHeadModel_firmament() != null)
|
||||
return false;
|
||||
return original.call(stack, slot);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import moe.nea.firmament.features.texturepack.TintOverrides;
|
||||
import moe.nea.firmament.init.ItemColorsSodiumRiser;
|
||||
import net.minecraft.client.color.item.ItemColorProvider;
|
||||
import net.minecraft.client.color.item.ItemColors;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(ItemColors.class)
|
||||
public class ItemColorRemovalPatch {
|
||||
|
||||
/**
|
||||
* @see ItemColorsSodiumRiser
|
||||
*/
|
||||
private @Nullable ItemColorProvider overrideSodium_firmament(@Nullable ItemColorProvider original) {
|
||||
var tintOverrides = TintOverrides.Companion.getCurrentOverrides();
|
||||
if (!tintOverrides.hasOverrides()) return original;
|
||||
return (stack, tintIndex) -> {
|
||||
var override = tintOverrides.getOverride(tintIndex);
|
||||
if (override != null) return override;
|
||||
if (original != null) return original.getColor(stack, tintIndex);
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Inject(method = "getColor", at = @At("HEAD"), cancellable = true)
|
||||
private void overrideGetColorCall(ItemStack item, int tintIndex, CallbackInfoReturnable<Integer> cir) {
|
||||
var tintOverrides = TintOverrides.Companion.getCurrentOverrides();
|
||||
var override = tintOverrides.getOverride(tintIndex);
|
||||
if (override != null)
|
||||
cir.setReturnValue(override);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.features.texturepack.JsonUnbakedModelFirmExtra;
|
||||
import net.minecraft.client.render.model.json.ItemModelGenerator;
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(ItemModelGenerator.class)
|
||||
public class ItemModelGeneratorJsonUnbakedModelCopy {
|
||||
@ModifyReturnValue(method = "create", at = @At("RETURN"))
|
||||
private JsonUnbakedModel copyExtraModelData(JsonUnbakedModel original, @Local(argsOnly = true) JsonUnbakedModel oldModel) {
|
||||
var extra = ((JsonUnbakedModelFirmExtra) original);
|
||||
var oldExtra = ((JsonUnbakedModelFirmExtra) oldModel);
|
||||
extra.setHeadModel_firmament(oldExtra.getHeadModel_firmament());
|
||||
extra.setTintOverrides_firmament(oldExtra.getTintOverrides_firmament());
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import moe.nea.firmament.features.texturepack.BakedModelExtra;
|
||||
import moe.nea.firmament.features.texturepack.TintOverrides;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.item.ItemRenderer;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ModelTransformationMode;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(value = ItemRenderer.class, priority = 1010)
|
||||
public class ItemRendererTintContextPatch {
|
||||
@Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;ZF)V",
|
||||
at = @At(value = "HEAD"), allow = 1)
|
||||
private void onStartRendering(ItemStack stack, ModelTransformationMode transformationMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, boolean useInventoryModel, float z, CallbackInfo ci) {
|
||||
var extra = BakedModelExtra.cast(model);
|
||||
if (extra != null) {
|
||||
TintOverrides.Companion.enter(extra.getTintOverrides_firmament());
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;ZF)V",
|
||||
at = @At("TAIL"), allow = 1)
|
||||
private void onEndRendering(ItemStack stack, ModelTransformationMode transformationMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, boolean useInventoryModel, float z, CallbackInfo ci) {
|
||||
var extra = BakedModelExtra.cast(model);
|
||||
if (extra != null) {
|
||||
TintOverrides.Companion.exit(extra.getTintOverrides_firmament());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.features.texturepack.BakedModelExtra;
|
||||
import moe.nea.firmament.features.texturepack.JsonUnbakedModelFirmExtra;
|
||||
import moe.nea.firmament.features.texturepack.TintOverrides;
|
||||
import moe.nea.firmament.util.ErrorUtil;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.Baker;
|
||||
import net.minecraft.client.render.model.ModelRotation;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Mixin(JsonUnbakedModel.class)
|
||||
public abstract class JsonUnbakedModelDataHolder implements JsonUnbakedModelFirmExtra {
|
||||
@Shadow
|
||||
@Nullable
|
||||
protected JsonUnbakedModel parent;
|
||||
|
||||
@Shadow
|
||||
public abstract String toString();
|
||||
|
||||
@Unique
|
||||
@Nullable
|
||||
public Identifier headModel;
|
||||
@Unique
|
||||
@Nullable
|
||||
public TintOverrides tintOverrides;
|
||||
@Unique
|
||||
@Nullable
|
||||
public TintOverrides mergedTintOverrides;
|
||||
|
||||
@Override
|
||||
public void setTintOverrides_firmament(@Nullable TintOverrides tintOverrides) {
|
||||
this.tintOverrides = tintOverrides;
|
||||
this.mergedTintOverrides = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TintOverrides getTintOverrides_firmament() {
|
||||
if (mergedTintOverrides != null)
|
||||
return mergedTintOverrides;
|
||||
var mergedTintOverrides = parent == null ? new TintOverrides()
|
||||
: ((JsonUnbakedModelFirmExtra) parent).getTintOverrides_firmament();
|
||||
if (tintOverrides != null)
|
||||
mergedTintOverrides = tintOverrides.mergeWithParent(mergedTintOverrides);
|
||||
this.mergedTintOverrides = mergedTintOverrides;
|
||||
return mergedTintOverrides;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeadModel_firmament(@Nullable Identifier identifier) {
|
||||
this.headModel = identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Identifier getHeadModel_firmament() {
|
||||
if (this.headModel != null) return this.headModel;
|
||||
if (this.parent == null) return null;
|
||||
return ((JsonUnbakedModelFirmExtra) this.parent).getHeadModel_firmament();
|
||||
}
|
||||
|
||||
@Inject(method = "resolve", at = @At("HEAD"))
|
||||
private void addDependencies(UnbakedModel.Resolver resolver, CallbackInfo ci) {
|
||||
var headModel = getHeadModel_firmament();
|
||||
if (headModel != null) {
|
||||
resolver.resolve(headModel);
|
||||
}
|
||||
}
|
||||
|
||||
private void addExtraBakeInfo(BakedModel bakedModel, Baker baker) {
|
||||
if (!this.toString().contains("minecraft") && this.toString().contains("crimson")) {
|
||||
System.out.println("Found non minecraft model " + this);
|
||||
}
|
||||
var extra = BakedModelExtra.cast(bakedModel);
|
||||
if (extra != null) {
|
||||
var headModel = getHeadModel_firmament();
|
||||
if (headModel != null) {
|
||||
extra.setHeadModel_firmament(baker.bake(headModel, ModelRotation.X0_Y0));
|
||||
}
|
||||
if (getTintOverrides_firmament().hasOverrides()) {
|
||||
extra.setTintOverrides_firmament(getTintOverrides_firmament());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ProvideBakerToJsonUnbakedModelPatch
|
||||
*/
|
||||
@Override
|
||||
public void storeExtraBaker_firmament(@NotNull Baker baker) {
|
||||
this.storedBaker = baker;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private Baker storedBaker;
|
||||
|
||||
@ModifyReturnValue(
|
||||
method = "bake(Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;Z)Lnet/minecraft/client/render/model/BakedModel;",
|
||||
at = @At("RETURN"))
|
||||
private BakedModel bakeExtraInfoWithoutBaker(BakedModel original) {
|
||||
if (storedBaker != null) {
|
||||
addExtraBakeInfo(original, storedBaker);
|
||||
storedBaker = null;
|
||||
}
|
||||
return original;
|
||||
}
|
||||
|
||||
@ModifyReturnValue(
|
||||
method = {
|
||||
"bake(Lnet/minecraft/client/render/model/Baker;Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;)Lnet/minecraft/client/render/model/BakedModel;"
|
||||
},
|
||||
at = @At(value = "RETURN"))
|
||||
private BakedModel bakeExtraInfo(BakedModel original, @Local(argsOnly = true) Baker baker) {
|
||||
addExtraBakeInfo(original, baker);
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate;
|
||||
import moe.nea.firmament.features.texturepack.ModelOverrideData;
|
||||
import net.minecraft.client.render.model.json.ModelOverride;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(ModelOverride.class)
|
||||
public class ModelOverrideDataHolder implements ModelOverrideData {
|
||||
|
||||
@Unique
|
||||
private FirmamentModelPredicate[] overrides;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FirmamentModelPredicate[] getFirmamentOverrides() {
|
||||
return overrides;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFirmamentOverrides(@NotNull FirmamentModelPredicate[] overrides) {
|
||||
this.overrides = overrides;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.features.texturepack.JsonUnbakedModelFirmExtra;
|
||||
import moe.nea.firmament.features.texturepack.TintOverrides;
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(JsonUnbakedModel.Deserializer.class)
|
||||
public class PatchJsonUnbakedModelDeserializer {
|
||||
@ModifyReturnValue(method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/render/model/json/JsonUnbakedModel;",
|
||||
at = @At("RETURN"))
|
||||
private JsonUnbakedModel addHeadModel(JsonUnbakedModel original, @Local JsonObject jsonObject) {
|
||||
var headModel = jsonObject.get("firmament:head_model");
|
||||
var extra = ((JsonUnbakedModelFirmExtra) original);
|
||||
if (headModel instanceof JsonPrimitive prim && prim.isString()) {
|
||||
extra.setHeadModel_firmament(Identifier.of(prim.getAsString()));
|
||||
}
|
||||
var tintOverrides = jsonObject.get("firmament:tint_overrides");
|
||||
if (tintOverrides instanceof JsonObject object) {
|
||||
extra.setTintOverrides_firmament(TintOverrides.Companion.parse(object));
|
||||
}
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import moe.nea.firmament.features.texturepack.CustomGlobalArmorOverrides;
|
||||
import net.minecraft.client.render.entity.equipment.EquipmentModelLoader;
|
||||
import net.minecraft.client.render.entity.equipment.EquipmentRenderer;
|
||||
import net.minecraft.item.equipment.EquipmentModel;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(EquipmentRenderer.class)
|
||||
public class PatchLegacyArmorLayerSupport {
|
||||
@WrapOperation(method = "render(Lnet/minecraft/item/equipment/EquipmentModel$LayerType;Lnet/minecraft/util/Identifier;Lnet/minecraft/client/model/Model;Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/util/Identifier;)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/equipment/EquipmentModelLoader;get(Lnet/minecraft/util/Identifier;)Lnet/minecraft/item/equipment/EquipmentModel;"))
|
||||
private EquipmentModel patchModelLayers(EquipmentModelLoader instance, Identifier id, Operation<EquipmentModel> original) {
|
||||
var modelOverride = CustomGlobalArmorOverrides.overrideArmorLayer(id);
|
||||
if (modelOverride != null) return modelOverride;
|
||||
return original.call(instance, id);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.features.texturepack.CustomModelOverrideParser;
|
||||
import moe.nea.firmament.features.texturepack.ModelOverrideData;
|
||||
import net.minecraft.client.render.model.json.ModelOverride;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ModelOverride.Deserializer.class)
|
||||
public class PatchOverrideDeserializer {
|
||||
|
||||
@ModifyReturnValue(
|
||||
method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/render/model/json/ModelOverride;",
|
||||
at = @At(value = "RETURN"))
|
||||
private ModelOverride addCustomOverrides(ModelOverride original, @Local JsonObject jsonObject) {
|
||||
var originalData = (ModelOverrideData) (Object) original;
|
||||
originalData.setFirmamentOverrides(CustomModelOverrideParser.parseCustomModelOverrides(jsonObject));
|
||||
return original;
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(
|
||||
method = "deserializeMinPropertyValues(Lcom/google/gson/JsonObject;)Ljava/util/List;",
|
||||
at = @At(value = "INVOKE", target = "Ljava/util/Map$Entry;getValue()Ljava/lang/Object;"))
|
||||
private Object removeFirmamentPredicatesFromJsonIteration(Object original, @Local Map.Entry<String, JsonElement> entry) {
|
||||
if (entry.getKey().startsWith("firmament:")) return new JsonPrimitive(0F);
|
||||
return original;
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "deserializeMinPropertyValues",
|
||||
at = @At(value = "INVOKE", target = "Ljava/util/Map;entrySet()Ljava/util/Set;")
|
||||
)
|
||||
private void whatever(JsonObject object, CallbackInfoReturnable<List<ModelOverride.Condition>> cir,
|
||||
@Local Map<Identifier, Float> maps) {
|
||||
maps.entrySet().removeIf(it -> it.getKey().getNamespace().equals("firmament"));
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import moe.nea.firmament.features.texturepack.JsonUnbakedModelFirmExtra;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.Baker;
|
||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @see JsonUnbakedModelDataHolder#storeExtraBaker_firmament
|
||||
*/
|
||||
@Mixin(targets = "net.minecraft.client.render.model.ModelBaker$BakerImpl")
|
||||
public abstract class ProvideBakerToJsonUnbakedModelPatch implements Baker {
|
||||
@WrapOperation(method = "bake(Lnet/minecraft/client/render/model/UnbakedModel;Lnet/minecraft/client/render/model/ModelBakeSettings;)Lnet/minecraft/client/render/model/BakedModel;", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/json/JsonUnbakedModel;bake(Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;Z)Lnet/minecraft/client/render/model/BakedModel;"))
|
||||
private BakedModel provideExtraBakerToModel(JsonUnbakedModel instance, Function<SpriteIdentifier, Sprite> function, ModelBakeSettings modelBakeSettings, boolean bl, Operation<BakedModel> original) {
|
||||
((JsonUnbakedModelFirmExtra) instance).storeExtraBaker_firmament(this);
|
||||
return original.call(instance, function, modelBakeSettings, bl);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.Firmament;
|
||||
import moe.nea.firmament.features.texturepack.BakedOverrideData;
|
||||
import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures;
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate;
|
||||
import moe.nea.firmament.features.texturepack.ModelOverrideData;
|
||||
import net.minecraft.client.render.model.json.ModelOverride;
|
||||
import net.minecraft.client.render.model.json.ModelOverrideList;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Mixin(ModelOverrideList.class)
|
||||
public class TestForFirmamentOverridePredicatesPatch {
|
||||
|
||||
@Shadow
|
||||
private Identifier[] conditionTypes;
|
||||
|
||||
@ModifyArg(method = "<init>(Lnet/minecraft/client/render/model/Baker;Ljava/util/List;)V",
|
||||
at = @At(
|
||||
value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z"
|
||||
))
|
||||
public Object onInit(
|
||||
Object element,
|
||||
@Local ModelOverride modelOverride
|
||||
) {
|
||||
var bakedOverride = (ModelOverrideList.BakedOverride) element;
|
||||
var modelOverrideData = ModelOverrideData.cast(modelOverride);
|
||||
BakedOverrideData.cast(bakedOverride)
|
||||
.setFirmamentOverrides(modelOverrideData.getFirmamentOverrides());
|
||||
if (conditionTypes.length == 0 &&
|
||||
modelOverrideData.getFirmamentOverrides() != null &&
|
||||
modelOverrideData.getFirmamentOverrides().length > 0) {
|
||||
conditionTypes = new Identifier[]{Firmament.INSTANCE.identifier("sentinel/enforce_model_override_evaluation")};
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(method = "getModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride;test([F)Z"))
|
||||
public boolean testFirmamentOverrides(boolean originalValue,
|
||||
@Local ModelOverrideList.BakedOverride bakedOverride,
|
||||
@Local(argsOnly = true) ItemStack stack) {
|
||||
if (!originalValue) return false;
|
||||
var overrideData = (BakedOverrideData) (Object) bakedOverride;
|
||||
var overrides = overrideData.getFirmamentOverrides();
|
||||
if (overrides == null) return true;
|
||||
if (!CustomSkyBlockTextures.TConfig.INSTANCE.getEnableModelOverrides()) return false;
|
||||
for (FirmamentModelPredicate firmamentOverride : overrides) {
|
||||
if (!firmamentOverride.test(stack))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package moe.nea.firmament.events
|
||||
|
||||
import java.util.function.BiConsumer
|
||||
import net.minecraft.client.item.ItemAssetsLoader
|
||||
import net.minecraft.client.render.model.ReferencedModelsCollector
|
||||
import net.minecraft.client.util.ModelIdentifier
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
// TODO: Rename this event, since it is not really directly baking models anymore
|
||||
// TODO: This event may be removed now since ItemAssetsLoader seems to load all item models now (probably to cope with servers setting the item_model component). Check whether this also applies to blocks now.
|
||||
//@Deprecated(level = DeprecationLevel.ERROR, message = "This is no longer needed, since ItemAssetsLoader loads all item models.")
|
||||
class BakeExtraModelsEvent(
|
||||
private val addAnyModel: BiConsumer<ModelIdentifier, Identifier>,
|
||||
) : FirmamentEvent() {
|
||||
@@ -15,10 +17,13 @@ class BakeExtraModelsEvent(
|
||||
}
|
||||
|
||||
fun addItemModel(modelIdentifier: ModelIdentifier) {
|
||||
addNonItemModel(
|
||||
modelIdentifier,
|
||||
modelIdentifier.id.withPrefixedPath(ReferencedModelsCollector.ITEM_DIRECTORY))
|
||||
// TODO: If this is still needed: ItemAssetsLoader.FINDER
|
||||
// addNonItemModel(
|
||||
// modelIdentifier,
|
||||
// modelIdentifier.id.withPrefixedPath())
|
||||
}
|
||||
|
||||
// @Deprecated(level = DeprecationLevel.ERROR, message = "This is no longer needed, since ItemAssetsLoader loads all item models.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : FirmamentEventBus<BakeExtraModelsEvent>()
|
||||
}
|
||||
|
||||
@@ -1,38 +1,23 @@
|
||||
package moe.nea.firmament.events
|
||||
|
||||
import java.util.Optional
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.client.render.item.ItemModels
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.util.ModelIdentifier
|
||||
import net.minecraft.item.ItemStack
|
||||
import moe.nea.firmament.util.ErrorUtil
|
||||
import moe.nea.firmament.util.collections.WeakCache
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
// TODO: assert an order on these events
|
||||
data class CustomItemModelEvent(
|
||||
val itemStack: ItemStack,
|
||||
var overrideModel: ModelIdentifier? = null,
|
||||
var overrideModel: Identifier? = null,
|
||||
) : FirmamentEvent() {
|
||||
companion object : FirmamentEventBus<CustomItemModelEvent>() {
|
||||
val cache =
|
||||
WeakCache.memoize<ItemStack, ItemModels, Optional<BakedModel>>("CustomItemModels") { stack, models ->
|
||||
val modelId = getModelIdentifier(stack) ?: return@memoize Optional.empty()
|
||||
ErrorUtil.softCheck("Model Id needs to have an inventory variant", modelId.variant() == "inventory")
|
||||
val bakedModel = models.getModel(modelId.id)
|
||||
if (bakedModel == null || bakedModel === models.missingModelSupplier.get()) return@memoize Optional.empty()
|
||||
Optional.of(bakedModel)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getModelIdentifier(itemStack: ItemStack?): ModelIdentifier? {
|
||||
fun getModelIdentifier(itemStack: ItemStack?): Identifier? {
|
||||
// TODO: Re-add memoization and add an error / warning if the model does not exist
|
||||
if (itemStack == null) return null
|
||||
return publish(CustomItemModelEvent(itemStack)).overrideModel
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getModel(itemStack: ItemStack?, thing: ItemModels): BakedModel? {
|
||||
if (itemStack == null) return null
|
||||
return cache.invoke(itemStack, thing).getOrNull()
|
||||
}
|
||||
fun overrideIfExists(overrideModel: Identifier) {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import moe.nea.firmament.features.inventory.buttons.InventoryButtons
|
||||
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlay
|
||||
import moe.nea.firmament.features.mining.PickaxeAbility
|
||||
import moe.nea.firmament.features.mining.PristineProfitTracker
|
||||
import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures
|
||||
import moe.nea.firmament.features.world.FairySouls
|
||||
import moe.nea.firmament.features.world.Waypoints
|
||||
import moe.nea.firmament.util.data.DataHolder
|
||||
@@ -70,7 +69,6 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
|
||||
loadFeature(QuickCommands)
|
||||
loadFeature(PetFeatures)
|
||||
loadFeature(SaveCursorPosition)
|
||||
loadFeature(CustomSkyBlockTextures)
|
||||
loadFeature(PriceData)
|
||||
loadFeature(Fixes)
|
||||
loadFeature(DianaWaypoints)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.features.chat
|
||||
|
||||
import io.ktor.client.request.get
|
||||
@@ -7,16 +5,15 @@ import io.ktor.client.statement.bodyAsChannel
|
||||
import io.ktor.utils.io.jvm.javaio.toInputStream
|
||||
import java.net.URL
|
||||
import java.util.Collections
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import moe.nea.jarvis.api.Point
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.async
|
||||
import kotlin.math.min
|
||||
import net.minecraft.client.gui.screen.ChatScreen
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.texture.NativeImage
|
||||
import net.minecraft.client.texture.NativeImageBackedTexture
|
||||
import net.minecraft.scoreboard.ScoreboardCriterion.RenderType
|
||||
import net.minecraft.text.ClickEvent
|
||||
import net.minecraft.text.HoverEvent
|
||||
import net.minecraft.text.Style
|
||||
@@ -35,130 +32,132 @@ import moe.nea.firmament.util.transformEachRecursively
|
||||
import moe.nea.firmament.util.unformattedString
|
||||
|
||||
object ChatLinks : FirmamentFeature {
|
||||
override val identifier: String
|
||||
get() = "chat-links"
|
||||
override val identifier: String
|
||||
get() = "chat-links"
|
||||
|
||||
object TConfig : ManagedConfig(identifier, Category.CHAT) {
|
||||
val enableLinks by toggle("links-enabled") { true }
|
||||
val imageEnabled by toggle("image-enabled") { true }
|
||||
val allowAllHosts by toggle("allow-all-hosts") { false }
|
||||
val allowedHosts by string("allowed-hosts") { "cdn.discordapp.com,media.discordapp.com,media.discordapp.net,i.imgur.com" }
|
||||
val actualAllowedHosts get() = allowedHosts.split(",").map { it.trim() }
|
||||
val position by position("position", 16 * 20, 9 * 20) { Point(0.0, 0.0) }
|
||||
}
|
||||
object TConfig : ManagedConfig(identifier, Category.CHAT) {
|
||||
val enableLinks by toggle("links-enabled") { true }
|
||||
val imageEnabled by toggle("image-enabled") { true }
|
||||
val allowAllHosts by toggle("allow-all-hosts") { false }
|
||||
val allowedHosts by string("allowed-hosts") { "cdn.discordapp.com,media.discordapp.com,media.discordapp.net,i.imgur.com" }
|
||||
val actualAllowedHosts get() = allowedHosts.split(",").map { it.trim() }
|
||||
val position by position("position", 16 * 20, 9 * 20) { Point(0.0, 0.0) }
|
||||
}
|
||||
|
||||
private fun isHostAllowed(host: String) =
|
||||
TConfig.allowAllHosts || TConfig.actualAllowedHosts.any { it.equals(host, ignoreCase = true) }
|
||||
private fun isHostAllowed(host: String) =
|
||||
TConfig.allowAllHosts || TConfig.actualAllowedHosts.any { it.equals(host, ignoreCase = true) }
|
||||
|
||||
private fun isUrlAllowed(url: String) = isHostAllowed(url.removePrefix("https://").substringBefore("/"))
|
||||
private fun isUrlAllowed(url: String) = isHostAllowed(url.removePrefix("https://").substringBefore("/"))
|
||||
|
||||
override val config get() = TConfig
|
||||
val urlRegex = "https://[^. ]+\\.[^ ]+(\\.?( |$))".toRegex()
|
||||
override val config get() = TConfig
|
||||
val urlRegex = "https://[^. ]+\\.[^ ]+(\\.?( |$))".toRegex()
|
||||
val nextTexId = AtomicInteger(0)
|
||||
|
||||
data class Image(
|
||||
val texture: Identifier,
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
)
|
||||
data class Image(
|
||||
val texture: Identifier,
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
)
|
||||
|
||||
val imageCache: MutableMap<String, Deferred<Image?>> =
|
||||
Collections.synchronizedMap(mutableMapOf<String, Deferred<Image?>>())
|
||||
val imageCache: MutableMap<String, Deferred<Image?>> =
|
||||
Collections.synchronizedMap(mutableMapOf<String, Deferred<Image?>>())
|
||||
|
||||
private fun tryCacheUrl(url: String) {
|
||||
if (!isUrlAllowed(url)) {
|
||||
return
|
||||
}
|
||||
if (url in imageCache) {
|
||||
return
|
||||
}
|
||||
imageCache[url] = Firmament.coroutineScope.async {
|
||||
try {
|
||||
val response = Firmament.httpClient.get(URL(url))
|
||||
if (response.status.value == 200) {
|
||||
val inputStream = response.bodyAsChannel().toInputStream(Firmament.globalJob)
|
||||
val image = NativeImage.read(inputStream)
|
||||
val texture = MC.textureManager.registerDynamicTexture(
|
||||
"dynamic_image_preview",
|
||||
NativeImageBackedTexture(image)
|
||||
)
|
||||
Image(texture, image.width, image.height)
|
||||
} else
|
||||
null
|
||||
} catch (exc: Exception) {
|
||||
exc.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun tryCacheUrl(url: String) {
|
||||
if (!isUrlAllowed(url)) {
|
||||
return
|
||||
}
|
||||
if (url in imageCache) {
|
||||
return
|
||||
}
|
||||
imageCache[url] = Firmament.coroutineScope.async {
|
||||
try {
|
||||
val response = Firmament.httpClient.get(URL(url))
|
||||
if (response.status.value == 200) {
|
||||
val inputStream = response.bodyAsChannel().toInputStream(Firmament.globalJob)
|
||||
val image = NativeImage.read(inputStream)
|
||||
val texId = Firmament.identifier("dynamic_image_preview${nextTexId.getAndIncrement()}")
|
||||
MC.textureManager.registerTexture(
|
||||
texId,
|
||||
NativeImageBackedTexture(image)
|
||||
)
|
||||
Image(texId, image.width, image.height)
|
||||
} else
|
||||
null
|
||||
} catch (exc: Exception) {
|
||||
exc.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val imageExtensions = listOf("jpg", "png", "gif", "jpeg")
|
||||
fun isImageUrl(url: String): Boolean {
|
||||
return (url.substringAfterLast('.').lowercase() in imageExtensions)
|
||||
}
|
||||
val imageExtensions = listOf("jpg", "png", "gif", "jpeg")
|
||||
fun isImageUrl(url: String): Boolean {
|
||||
return (url.substringAfterLast('.').lowercase() in imageExtensions)
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun onRender(it: ScreenRenderPostEvent) {
|
||||
if (!TConfig.imageEnabled) return
|
||||
if (it.screen !is ChatScreen) return
|
||||
val hoveredComponent =
|
||||
MC.inGameHud.chatHud.getTextStyleAt(it.mouseX.toDouble(), it.mouseY.toDouble()) ?: return
|
||||
val hoverEvent = hoveredComponent.hoverEvent ?: return
|
||||
val value = hoverEvent.getValue(HoverEvent.Action.SHOW_TEXT) ?: return
|
||||
val url = urlRegex.matchEntire(value.unformattedString)?.groupValues?.get(0) ?: return
|
||||
if (!isImageUrl(url)) return
|
||||
val imageFuture = imageCache[url] ?: return
|
||||
if (!imageFuture.isCompleted) return
|
||||
val image = imageFuture.getCompleted() ?: return
|
||||
it.drawContext.matrices.push()
|
||||
val pos = TConfig.position
|
||||
pos.applyTransformations(it.drawContext.matrices)
|
||||
val scale = min(1F, min((9 * 20F) / image.height, (16 * 20F) / image.width))
|
||||
it.drawContext.matrices.scale(scale, scale, 1F)
|
||||
it.drawContext.drawTexture(
|
||||
image.texture,
|
||||
0,
|
||||
0,
|
||||
1F,
|
||||
1F,
|
||||
image.width,
|
||||
image.height,
|
||||
image.width,
|
||||
image.height,
|
||||
)
|
||||
it.drawContext.matrices.pop()
|
||||
}
|
||||
@Subscribe
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun onRender(it: ScreenRenderPostEvent) {
|
||||
if (!TConfig.imageEnabled) return
|
||||
if (it.screen !is ChatScreen) return
|
||||
val hoveredComponent =
|
||||
MC.inGameHud.chatHud.getTextStyleAt(it.mouseX.toDouble(), it.mouseY.toDouble()) ?: return
|
||||
val hoverEvent = hoveredComponent.hoverEvent ?: return
|
||||
val value = hoverEvent.getValue(HoverEvent.Action.SHOW_TEXT) ?: return
|
||||
val url = urlRegex.matchEntire(value.unformattedString)?.groupValues?.get(0) ?: return
|
||||
if (!isImageUrl(url)) return
|
||||
val imageFuture = imageCache[url] ?: return
|
||||
if (!imageFuture.isCompleted) return
|
||||
val image = imageFuture.getCompleted() ?: return
|
||||
it.drawContext.matrices.push()
|
||||
val pos = TConfig.position
|
||||
pos.applyTransformations(it.drawContext.matrices)
|
||||
val scale = min(1F, min((9 * 20F) / image.height, (16 * 20F) / image.width))
|
||||
it.drawContext.matrices.scale(scale, scale, 1F)
|
||||
it.drawContext.drawTexture(
|
||||
image.texture,
|
||||
0,
|
||||
0,
|
||||
1F,
|
||||
1F,
|
||||
image.width,
|
||||
image.height,
|
||||
image.width,
|
||||
image.height,
|
||||
)
|
||||
it.drawContext.matrices.pop()
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onModifyChat(it: ModifyChatEvent) {
|
||||
if (!TConfig.enableLinks) return
|
||||
it.replaceWith = it.replaceWith.transformEachRecursively { child ->
|
||||
val text = child.string
|
||||
if ("://" !in text) return@transformEachRecursively child
|
||||
val s = Text.empty().setStyle(child.style)
|
||||
var index = 0
|
||||
while (index < text.length) {
|
||||
val nextMatch = urlRegex.find(text, index)
|
||||
if (nextMatch == null) {
|
||||
s.append(Text.literal(text.substring(index, text.length)))
|
||||
break
|
||||
}
|
||||
val range = nextMatch.groups[0]!!.range
|
||||
val url = nextMatch.groupValues[0]
|
||||
s.append(Text.literal(text.substring(index, range.first)))
|
||||
s.append(
|
||||
Text.literal(url).setStyle(
|
||||
Style.EMPTY.withUnderline(true).withColor(
|
||||
Formatting.AQUA
|
||||
).withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(url)))
|
||||
.withClickEvent(ClickEvent(ClickEvent.Action.OPEN_URL, url))
|
||||
)
|
||||
)
|
||||
if (isImageUrl(url))
|
||||
tryCacheUrl(url)
|
||||
index = range.last + 1
|
||||
}
|
||||
s
|
||||
}
|
||||
}
|
||||
@Subscribe
|
||||
fun onModifyChat(it: ModifyChatEvent) {
|
||||
if (!TConfig.enableLinks) return
|
||||
it.replaceWith = it.replaceWith.transformEachRecursively { child ->
|
||||
val text = child.string
|
||||
if ("://" !in text) return@transformEachRecursively child
|
||||
val s = Text.empty().setStyle(child.style)
|
||||
var index = 0
|
||||
while (index < text.length) {
|
||||
val nextMatch = urlRegex.find(text, index)
|
||||
if (nextMatch == null) {
|
||||
s.append(Text.literal(text.substring(index, text.length)))
|
||||
break
|
||||
}
|
||||
val range = nextMatch.groups[0]!!.range
|
||||
val url = nextMatch.groupValues[0]
|
||||
s.append(Text.literal(text.substring(index, range.first)))
|
||||
s.append(
|
||||
Text.literal(url).setStyle(
|
||||
Style.EMPTY.withUnderline(true).withColor(
|
||||
Formatting.AQUA
|
||||
).withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(url)))
|
||||
.withClickEvent(ClickEvent(ClickEvent.Action.OPEN_URL, url))
|
||||
)
|
||||
)
|
||||
if (isImageUrl(url))
|
||||
tryCacheUrl(url)
|
||||
index = range.last + 1
|
||||
}
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.block.SkullBlock
|
||||
import net.minecraft.block.entity.SkullBlockEntity
|
||||
import net.minecraft.component.DataComponentTypes
|
||||
import net.minecraft.component.type.ProfileComponent
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
@@ -12,6 +13,7 @@ import net.minecraft.item.Items
|
||||
import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.text.TextCodecs
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.hit.EntityHitResult
|
||||
import net.minecraft.util.hit.HitResult
|
||||
@@ -23,7 +25,6 @@ import moe.nea.firmament.events.ScreenChangeEvent
|
||||
import moe.nea.firmament.events.TickEvent
|
||||
import moe.nea.firmament.events.WorldKeyboardEvent
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures
|
||||
import moe.nea.firmament.gui.config.ManagedConfig
|
||||
import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
|
||||
import moe.nea.firmament.util.ClipboardUtils
|
||||
@@ -101,6 +102,8 @@ object PowerUserTools : FirmamentFeature {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: leak this through some other way, maybe.
|
||||
lateinit var getSkullId: (profile: ProfileComponent) -> Identifier?
|
||||
|
||||
@Subscribe
|
||||
fun copyInventoryInfo(it: HandledScreenKeyPressedEvent) {
|
||||
@@ -116,7 +119,7 @@ object PowerUserTools : FirmamentFeature {
|
||||
lastCopiedStack =
|
||||
Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skyblockid", sbId.neuItem))
|
||||
} else if (it.matches(TConfig.copyTexturePackId)) {
|
||||
val model = CustomItemModelEvent.getModelIdentifier(item)
|
||||
val model = CustomItemModelEvent.getModelIdentifier(item) // TODO: remove global texture overrides, maybe
|
||||
if (model == null) {
|
||||
lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.modelid.fail"))
|
||||
return
|
||||
@@ -146,7 +149,7 @@ object PowerUserTools : FirmamentFeature {
|
||||
lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-profile"))
|
||||
return
|
||||
}
|
||||
val skullTexture = CustomSkyBlockTextures.getSkullTexture(profile)
|
||||
val skullTexture = getSkullId(profile)
|
||||
if (skullTexture == null) {
|
||||
lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-texture"))
|
||||
return
|
||||
@@ -179,7 +182,7 @@ object PowerUserTools : FirmamentFeature {
|
||||
MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail"))
|
||||
return
|
||||
}
|
||||
val id = CustomSkyBlockTextures.getSkullTexture(entity.owner!!)
|
||||
val id = getSkullId(entity.owner!!)
|
||||
if (id == null) {
|
||||
MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail"))
|
||||
} else {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.WrapperBakedModel as WrapperBakedModelFabric
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.WrapperBakedModel
|
||||
import moe.nea.firmament.util.ErrorUtil
|
||||
|
||||
interface BakedModelExtra {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun cast(originalModel: BakedModel): BakedModelExtra? {
|
||||
var p = originalModel
|
||||
for (i in 0..256) {
|
||||
p = when (p) {
|
||||
is BakedModelExtra -> return p
|
||||
is WrapperBakedModel -> p.wrapped
|
||||
is WrapperBakedModelFabric -> WrapperBakedModelFabric.unwrap(p)
|
||||
else -> break
|
||||
}
|
||||
}
|
||||
ErrorUtil.softError("Could not find a baked model for $originalModel")
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
var tintOverrides_firmament: TintOverrides?
|
||||
|
||||
fun getHeadModel_firmament(): BakedModel?
|
||||
fun setHeadModel_firmament(headModel: BakedModel?)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import net.minecraft.client.render.model.json.ModelOverrideList
|
||||
|
||||
interface BakedOverrideData {
|
||||
fun getFirmamentOverrides(): Array<FirmamentModelPredicate>?
|
||||
fun setFirmamentOverrides(overrides: Array<FirmamentModelPredicate>?)
|
||||
companion object{
|
||||
@Suppress("CAST_NEVER_SUCCEEDS")
|
||||
@JvmStatic
|
||||
fun cast(bakedOverride: ModelOverrideList.BakedOverride): BakedOverrideData = bakedOverride as BakedOverrideData
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import moe.nea.firmament.features.texturepack.predicates.AndPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.DisplayNamePredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.ExtraAttributesPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.ItemPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.LorePredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.NotPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.OrPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.PetPredicate
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
object CustomModelOverrideParser {
|
||||
object FirmamentRootPredicateSerializer : KSerializer<FirmamentModelPredicate> {
|
||||
val delegateSerializer = kotlinx.serialization.json.JsonObject.serializer()
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = SerialDescriptor("FirmamentModelRootPredicate", delegateSerializer.descriptor)
|
||||
|
||||
override fun deserialize(decoder: Decoder): FirmamentModelPredicate {
|
||||
val json = decoder.decodeSerializableValue(delegateSerializer).intoGson() as JsonObject
|
||||
return AndPredicate(parsePredicates(json).toTypedArray())
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: FirmamentModelPredicate) {
|
||||
TODO("Cannot serialize firmament predicates")
|
||||
}
|
||||
}
|
||||
|
||||
val predicateParsers = mutableMapOf<Identifier, FirmamentModelPredicateParser>()
|
||||
|
||||
|
||||
fun registerPredicateParser(name: String, parser: FirmamentModelPredicateParser) {
|
||||
predicateParsers[Identifier.of("firmament", name)] = parser
|
||||
}
|
||||
|
||||
init {
|
||||
registerPredicateParser("display_name", DisplayNamePredicate.Parser)
|
||||
registerPredicateParser("lore", LorePredicate.Parser)
|
||||
registerPredicateParser("all", AndPredicate.Parser)
|
||||
registerPredicateParser("any", OrPredicate.Parser)
|
||||
registerPredicateParser("not", NotPredicate.Parser)
|
||||
registerPredicateParser("item", ItemPredicate.Parser)
|
||||
registerPredicateParser("extra_attributes", ExtraAttributesPredicate.Parser)
|
||||
registerPredicateParser("pet", PetPredicate.Parser)
|
||||
}
|
||||
|
||||
private val neverPredicate = listOf(
|
||||
object : FirmamentModelPredicate {
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
fun parsePredicates(predicates: JsonObject): List<FirmamentModelPredicate> {
|
||||
val parsedPredicates = mutableListOf<FirmamentModelPredicate>()
|
||||
for (predicateName in predicates.keySet()) {
|
||||
if (!predicateName.startsWith("firmament:")) continue
|
||||
val identifier = Identifier.of(predicateName)
|
||||
val parser = predicateParsers[identifier] ?: return neverPredicate
|
||||
val parsedPredicate = parser.parse(predicates[predicateName]) ?: return neverPredicate
|
||||
parsedPredicates.add(parsedPredicate)
|
||||
}
|
||||
return parsedPredicates
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseCustomModelOverrides(jsonObject: JsonObject): Array<FirmamentModelPredicate>? {
|
||||
val predicates = (jsonObject["predicate"] as? JsonObject) ?: return null
|
||||
val parsedPredicates = parsePredicates(predicates)
|
||||
if (parsedPredicates.isEmpty())
|
||||
return null
|
||||
return parsedPredicates.toTypedArray()
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture
|
||||
import com.mojang.authlib.properties.Property
|
||||
import java.util.Optional
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.block.SkullBlock
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.util.ModelIdentifier
|
||||
import net.minecraft.component.type.ProfileComponent
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.BakeExtraModelsEvent
|
||||
import moe.nea.firmament.events.CustomItemModelEvent
|
||||
import moe.nea.firmament.events.FinalizeResourceManagerEvent
|
||||
import moe.nea.firmament.events.TickEvent
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
import moe.nea.firmament.gui.config.ManagedConfig
|
||||
import moe.nea.firmament.util.collections.WeakCache
|
||||
import moe.nea.firmament.util.mc.decodeProfileTextureProperty
|
||||
import moe.nea.firmament.util.skyBlockId
|
||||
|
||||
object CustomSkyBlockTextures : FirmamentFeature {
|
||||
override val identifier: String
|
||||
get() = "custom-skyblock-textures"
|
||||
|
||||
object TConfig : ManagedConfig(identifier, Category.INTEGRATIONS) { // TODO: should this be its own thing?
|
||||
val enabled by toggle("enabled") { true }
|
||||
val skullsEnabled by toggle("skulls-enabled") { true }
|
||||
val cacheForever by toggle("cache-forever") { true }
|
||||
val cacheDuration by integer("cache-duration", 0, 100) { 1 }
|
||||
val enableModelOverrides by toggle("model-overrides") { true }
|
||||
val enableArmorOverrides by toggle("armor-overrides") { true }
|
||||
val enableBlockOverrides by toggle("block-overrides") { true }
|
||||
val enableLegacyCIT by toggle("legacy-cit") { true }
|
||||
val allowRecoloringUiText by toggle("recolor-text") { true }
|
||||
}
|
||||
|
||||
override val config: ManagedConfig
|
||||
get() = TConfig
|
||||
|
||||
val allItemCaches by lazy {
|
||||
listOf(
|
||||
CustomItemModelEvent.cache.cache,
|
||||
skullTextureCache.cache,
|
||||
CustomGlobalTextures.overrideCache.cache,
|
||||
CustomGlobalArmorOverrides.overrideCache.cache
|
||||
)
|
||||
}
|
||||
|
||||
fun clearAllCaches() {
|
||||
allItemCaches.forEach(WeakCache<*, *, *>::clear)
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onTick(it: TickEvent) {
|
||||
if (TConfig.cacheForever) return
|
||||
if (TConfig.cacheDuration < 1 || it.tickCount % TConfig.cacheDuration == 0) {
|
||||
clearAllCaches()
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onStart(event: FinalizeResourceManagerEvent) {
|
||||
event.registerOnApply("Clear firmament CIT caches") {
|
||||
clearAllCaches()
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun bakeCustomFirmModels(event: BakeExtraModelsEvent) {
|
||||
val resources =
|
||||
MinecraftClient.getInstance().resourceManager.findResources("models/item"
|
||||
) { it: Identifier ->
|
||||
"firmskyblock" == it.namespace && it.path
|
||||
.endsWith(".json")
|
||||
}
|
||||
for (identifier in resources.keys) {
|
||||
val modelId = ModelIdentifier.ofInventoryVariant(
|
||||
Identifier.of(
|
||||
"firmskyblock",
|
||||
identifier.path.substring(
|
||||
"models/item/".length,
|
||||
identifier.path.length - ".json".length),
|
||||
))
|
||||
event.addItemModel(modelId)
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onCustomModelId(it: CustomItemModelEvent) {
|
||||
if (!TConfig.enabled) return
|
||||
val id = it.itemStack.skyBlockId ?: return
|
||||
it.overrideModel = ModelIdentifier.ofInventoryVariant(Identifier.of("firmskyblock", id.identifier.path))
|
||||
}
|
||||
|
||||
private val skullTextureCache =
|
||||
WeakCache.memoize<ProfileComponent, Optional<Identifier>>("SkullTextureCache") { component ->
|
||||
val id = getSkullTexture(component) ?: return@memoize Optional.empty()
|
||||
if (!MinecraftClient.getInstance().resourceManager.getResource(id).isPresent) {
|
||||
return@memoize Optional.empty()
|
||||
}
|
||||
return@memoize Optional.of(id)
|
||||
}
|
||||
|
||||
private val mcUrlRegex = "https?://textures.minecraft.net/texture/([a-fA-F0-9]+)".toRegex()
|
||||
|
||||
fun getSkullId(textureProperty: Property): String? {
|
||||
val texture = decodeProfileTextureProperty(textureProperty) ?: return null
|
||||
val textureUrl =
|
||||
texture.textures[MinecraftProfileTexture.Type.SKIN]?.url ?: return null
|
||||
val mcUrlData = mcUrlRegex.matchEntire(textureUrl) ?: return null
|
||||
return mcUrlData.groupValues[1]
|
||||
}
|
||||
|
||||
fun getSkullTexture(profile: ProfileComponent): Identifier? {
|
||||
val id = getSkullId(profile.properties["textures"].firstOrNull() ?: return null) ?: return null
|
||||
return Identifier.of("firmskyblock", "textures/placedskull/$id.png")
|
||||
}
|
||||
|
||||
fun modifySkullTexture(
|
||||
type: SkullBlock.SkullType?,
|
||||
component: ProfileComponent?,
|
||||
cir: CallbackInfoReturnable<RenderLayer>
|
||||
) {
|
||||
if (type != SkullBlock.Type.PLAYER) return
|
||||
if (!TConfig.skullsEnabled) return
|
||||
if (component == null) return
|
||||
|
||||
val n = skullTextureCache.invoke(component).getOrNull() ?: return
|
||||
cir.returnValue = RenderLayer.getEntityTranslucent(n)
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import net.minecraft.client.render.model.Baker
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
interface JsonUnbakedModelFirmExtra {
|
||||
fun storeExtraBaker_firmament(baker: Baker)
|
||||
|
||||
fun setHeadModel_firmament(identifier: Identifier?)
|
||||
fun getHeadModel_firmament(): Identifier?
|
||||
|
||||
fun setTintOverrides_firmament(tintOverrides: TintOverrides?)
|
||||
fun getTintOverrides_firmament(): TintOverrides
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import net.minecraft.client.render.model.json.ModelOverride
|
||||
|
||||
interface ModelOverrideData {
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
@Suppress("CAST_NEVER_SUCCEEDS")
|
||||
fun cast(override: ModelOverride) = override as ModelOverrideData
|
||||
}
|
||||
|
||||
fun getFirmamentOverrides(): Array<FirmamentModelPredicate>?
|
||||
fun setFirmamentOverrides(overrides: Array<FirmamentModelPredicate>?)
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.gui.screen.world.SelectWorldScreen
|
||||
import net.minecraft.component.type.MapIdComponent
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.boss.dragon.EnderDragonPart
|
||||
import net.minecraft.entity.damage.DamageSource
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.Fluid
|
||||
@@ -262,6 +263,10 @@ class FakeWorld(
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getEnderDragonParts(): MutableCollection<EnderDragonPart> {
|
||||
return mutableListOf()
|
||||
}
|
||||
|
||||
override fun getTickManager(): TickManager {
|
||||
return TickManager()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
package moe.nea.firmament.repo
|
||||
|
||||
import java.io.InputStream
|
||||
@@ -21,86 +20,86 @@ import net.minecraft.resource.ResourcePackInfo
|
||||
import net.minecraft.resource.ResourcePackSource
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.resource.metadata.ResourceMetadata
|
||||
import net.minecraft.resource.metadata.ResourceMetadataReader
|
||||
import net.minecraft.resource.metadata.ResourceMetadataSerializer
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.PathUtil
|
||||
import moe.nea.firmament.Firmament
|
||||
|
||||
class RepoModResourcePack(val basePath: Path) : ModResourcePack {
|
||||
companion object {
|
||||
fun append(packs: MutableList<in ModResourcePack>) {
|
||||
Firmament.logger.info("Registering mod resource pack")
|
||||
packs.add(RepoModResourcePack(RepoDownloadManager.repoSavedLocation))
|
||||
}
|
||||
companion object {
|
||||
fun append(packs: MutableList<in ModResourcePack>) {
|
||||
Firmament.logger.info("Registering mod resource pack")
|
||||
packs.add(RepoModResourcePack(RepoDownloadManager.repoSavedLocation))
|
||||
}
|
||||
|
||||
fun createResourceDirectly(identifier: Identifier): Optional<Resource> {
|
||||
val pack = RepoModResourcePack(RepoDownloadManager.repoSavedLocation)
|
||||
return Optional.of(
|
||||
Resource(
|
||||
pack,
|
||||
pack.open(ResourceType.CLIENT_RESOURCES, identifier) ?: return Optional.empty()
|
||||
) {
|
||||
val base =
|
||||
pack.open(ResourceType.CLIENT_RESOURCES, identifier.withPath(identifier.path + ".mcmeta"))
|
||||
if (base == null)
|
||||
ResourceMetadata.NONE
|
||||
else
|
||||
NamespaceResourceManager.loadMetadata(base)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
fun createResourceDirectly(identifier: Identifier): Optional<Resource> {
|
||||
val pack = RepoModResourcePack(RepoDownloadManager.repoSavedLocation)
|
||||
return Optional.of(
|
||||
Resource(
|
||||
pack,
|
||||
pack.open(ResourceType.CLIENT_RESOURCES, identifier) ?: return Optional.empty()
|
||||
) {
|
||||
val base =
|
||||
pack.open(ResourceType.CLIENT_RESOURCES, identifier.withPath(identifier.path + ".mcmeta"))
|
||||
if (base == null)
|
||||
ResourceMetadata.NONE
|
||||
else
|
||||
NamespaceResourceManager.loadMetadata(base)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
}
|
||||
override fun close() {
|
||||
}
|
||||
|
||||
override fun openRoot(vararg segments: String): InputSupplier<InputStream>? {
|
||||
return getFile(segments)?.let { InputSupplier.create(it) }
|
||||
}
|
||||
override fun openRoot(vararg segments: String): InputSupplier<InputStream>? {
|
||||
return getFile(segments)?.let { InputSupplier.create(it) }
|
||||
}
|
||||
|
||||
fun getFile(segments: Array<out String>): Path? {
|
||||
PathUtil.validatePath(*segments)
|
||||
val path = segments.fold(basePath, Path::resolve)
|
||||
if (!path.isRegularFile()) return null
|
||||
return path
|
||||
}
|
||||
fun getFile(segments: Array<out String>): Path? {
|
||||
PathUtil.validatePath(*segments)
|
||||
val path = segments.fold(basePath, Path::resolve)
|
||||
if (!path.isRegularFile()) return null
|
||||
return path
|
||||
}
|
||||
|
||||
override fun open(type: ResourceType?, id: Identifier): InputSupplier<InputStream>? {
|
||||
if (type != ResourceType.CLIENT_RESOURCES) return null
|
||||
if (id.namespace != "neurepo") return null
|
||||
val file = getFile(id.path.split("/").toTypedArray())
|
||||
return file?.let { InputSupplier.create(it) }
|
||||
}
|
||||
override fun open(type: ResourceType?, id: Identifier): InputSupplier<InputStream>? {
|
||||
if (type != ResourceType.CLIENT_RESOURCES) return null
|
||||
if (id.namespace != "neurepo") return null
|
||||
val file = getFile(id.path.split("/").toTypedArray())
|
||||
return file?.let { InputSupplier.create(it) }
|
||||
}
|
||||
|
||||
override fun findResources(
|
||||
type: ResourceType?,
|
||||
namespace: String,
|
||||
prefix: String,
|
||||
consumer: ResourcePack.ResultConsumer
|
||||
) {
|
||||
if (namespace != "neurepo") return
|
||||
if (type != ResourceType.CLIENT_RESOURCES) return
|
||||
override fun findResources(
|
||||
type: ResourceType?,
|
||||
namespace: String,
|
||||
prefix: String,
|
||||
consumer: ResourcePack.ResultConsumer
|
||||
) {
|
||||
if (namespace != "neurepo") return
|
||||
if (type != ResourceType.CLIENT_RESOURCES) return
|
||||
|
||||
val prefixPath = basePath.resolve(prefix)
|
||||
if (!prefixPath.exists())
|
||||
return
|
||||
Files.walk(prefixPath)
|
||||
.asSequence()
|
||||
.map { it.relativeTo(basePath) }
|
||||
.forEach {
|
||||
consumer.accept(Identifier.of("neurepo", it.toString()), InputSupplier.create(it))
|
||||
}
|
||||
}
|
||||
val prefixPath = basePath.resolve(prefix)
|
||||
if (!prefixPath.exists())
|
||||
return
|
||||
Files.walk(prefixPath)
|
||||
.asSequence()
|
||||
.map { it.relativeTo(basePath) }
|
||||
.forEach {
|
||||
consumer.accept(Identifier.of("neurepo", it.toString()), InputSupplier.create(it))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getNamespaces(type: ResourceType?): Set<String> {
|
||||
if (type != ResourceType.CLIENT_RESOURCES) return emptySet()
|
||||
return setOf("neurepo")
|
||||
}
|
||||
override fun getNamespaces(type: ResourceType?): Set<String> {
|
||||
if (type != ResourceType.CLIENT_RESOURCES) return emptySet()
|
||||
return setOf("neurepo")
|
||||
}
|
||||
|
||||
override fun <T> parseMetadata(metaReader: ResourceMetadataReader<T>): T? {
|
||||
return AbstractFileResourcePack.parseMetadata(
|
||||
metaReader, """
|
||||
override fun <T : Any?> parseMetadata(metadataSerializer: ResourceMetadataSerializer<T>?): T? {
|
||||
return AbstractFileResourcePack.parseMetadata(
|
||||
metadataSerializer, """
|
||||
{
|
||||
"pack": {
|
||||
"pack_format": 12,
|
||||
@@ -108,19 +107,20 @@ class RepoModResourcePack(val basePath: Path) : ModResourcePack {
|
||||
}
|
||||
}
|
||||
""".trimIndent().byteInputStream()
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun getInfo(): ResourcePackInfo {
|
||||
return ResourcePackInfo("neurepo", Text.literal("NEU Repo"), ResourcePackSource.BUILTIN, Optional.empty())
|
||||
}
|
||||
|
||||
override fun getFabricModMetadata(): ModMetadata {
|
||||
return FabricLoader.getInstance().getModContainer("firmament")
|
||||
.get().metadata
|
||||
}
|
||||
override fun getInfo(): ResourcePackInfo {
|
||||
return ResourcePackInfo("neurepo", Text.literal("NEU Repo"), ResourcePackSource.BUILTIN, Optional.empty())
|
||||
}
|
||||
|
||||
override fun createOverlay(overlay: String): ModResourcePack {
|
||||
return RepoModResourcePack(basePath.resolve(overlay))
|
||||
}
|
||||
override fun getFabricModMetadata(): ModMetadata {
|
||||
return FabricLoader.getInstance().getModContainer("firmament")
|
||||
.get().metadata
|
||||
}
|
||||
|
||||
override fun createOverlay(overlay: String): ModResourcePack {
|
||||
return RepoModResourcePack(basePath.resolve(overlay))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,6 @@ accessible class net/minecraft/client/render/RenderLayer$MultiPhaseParameters
|
||||
accessible class net/minecraft/client/font/TextRenderer$Drawer
|
||||
accessible field net/minecraft/client/gui/hud/InGameHud SCOREBOARD_ENTRY_COMPARATOR Ljava/util/Comparator;
|
||||
|
||||
accessible field net/minecraft/client/render/item/HeldItemRenderer itemRenderer Lnet/minecraft/client/render/item/ItemRenderer;
|
||||
accessible field net/minecraft/client/render/item/ItemModels missingModelSupplier Ljava/util/function/Supplier;
|
||||
mutable field net/minecraft/client/render/model/json/ModelOverrideList conditionTypes [Lnet/minecraft/util/Identifier;
|
||||
|
||||
accessible class net/minecraft/client/render/model/json/ModelOverride$Deserializer
|
||||
accessible class net/minecraft/client/render/model/json/ModelOverrideList$BakedOverride
|
||||
accessible field net/minecraft/entity/mob/CreeperEntity CHARGED Lnet/minecraft/entity/data/TrackedData;
|
||||
accessible method net/minecraft/entity/decoration/ArmorStandEntity setSmall (Z)V
|
||||
accessible field net/minecraft/entity/passive/AbstractHorseEntity items Lnet/minecraft/inventory/SimpleInventory;
|
||||
@@ -23,16 +17,3 @@ 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;
|
||||
|
||||
# Fix package-private access methods
|
||||
accessible method net/minecraft/registry/entry/RegistryEntry$Reference setRegistryKey (Lnet/minecraft/registry/RegistryKey;)V
|
||||
accessible method net/minecraft/entity/LivingEntity getHitbox ()Lnet/minecraft/util/math/Box;
|
||||
accessible method net/minecraft/registry/entry/RegistryEntryList$Named <init> (Lnet/minecraft/registry/entry/RegistryEntryOwner;Lnet/minecraft/registry/tag/TagKey;)V
|
||||
accessible method net/minecraft/registry/entry/RegistryEntry$Reference setValue (Ljava/lang/Object;)V
|
||||
accessible field net/minecraft/client/render/model/WrapperBakedModel wrapped Lnet/minecraft/client/render/model/BakedModel;
|
||||
accessible method net/minecraft/entity/passive/TameableEntity isInSameTeam (Lnet/minecraft/entity/Entity;)Z
|
||||
accessible method net/minecraft/entity/Entity isInSameTeam (Lnet/minecraft/entity/Entity;)Z
|
||||
accessible method net/minecraft/registry/entry/RegistryEntry$Reference setTags (Ljava/util/Collection;)V
|
||||
accessible method net/minecraft/registry/entry/RegistryEntryList$Named setEntries (Ljava/util/List;)V
|
||||
accessible method net/minecraft/world/biome/source/util/VanillaBiomeParameters writeOverworldBiomeParameters (Ljava/util/function/Consumer;)V
|
||||
accessible method net/minecraft/world/gen/densityfunction/DensityFunctions createSurfaceNoiseRouter (Lnet/minecraft/registry/RegistryEntryLookup;Lnet/minecraft/registry/RegistryEntryLookup;ZZ)Lnet/minecraft/world/gen/noise/NoiseRouter;
|
||||
|
||||
@@ -46,9 +46,9 @@ import moe.nea.firmament.util.json.SingletonSerializableList
|
||||
object CustomBlockTextures {
|
||||
@Serializable
|
||||
data class CustomBlockOverride(
|
||||
val modes: @Serializable(SingletonSerializableList::class) List<String>,
|
||||
val area: List<Area>? = null,
|
||||
val replacements: Map<Identifier, Replacement>,
|
||||
val modes: @Serializable(SingletonSerializableList::class) List<String>,
|
||||
val area: List<Area>? = null,
|
||||
val replacements: Map<Identifier, Replacement>,
|
||||
)
|
||||
|
||||
@Serializable(with = Replacement.Serializer::class)
|
||||
@@ -135,8 +135,8 @@ object CustomBlockTextures {
|
||||
)
|
||||
|
||||
data class BlockReplacement(
|
||||
val checks: List<Area>?,
|
||||
val replacement: Replacement,
|
||||
val checks: List<Area>?,
|
||||
val replacement: Replacement,
|
||||
) {
|
||||
val roughCheck by lazy(LazyThreadSafetyMode.NONE) {
|
||||
if (checks == null || checks.size < 3) return@lazy null
|
||||
@@ -8,8 +8,12 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import net.minecraft.client.render.entity.equipment.EquipmentModel
|
||||
import net.minecraft.component.type.EquippableComponent
|
||||
import net.minecraft.entity.EquipmentSlot
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.equipment.EquipmentModel
|
||||
import net.minecraft.item.equipment.EquipmentAssetKeys
|
||||
import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.resource.SinglePreparationResourceReloader
|
||||
import net.minecraft.util.Identifier
|
||||
@@ -20,6 +24,7 @@ import moe.nea.firmament.events.FinalizeResourceManagerEvent
|
||||
import moe.nea.firmament.features.texturepack.CustomGlobalTextures.logger
|
||||
import moe.nea.firmament.util.IdentifierSerializer
|
||||
import moe.nea.firmament.util.collections.WeakCache
|
||||
import moe.nea.firmament.util.intoOptional
|
||||
import moe.nea.firmament.util.skyBlockId
|
||||
|
||||
object CustomGlobalArmorOverrides {
|
||||
@@ -33,9 +38,9 @@ object CustomGlobalArmorOverrides {
|
||||
) {
|
||||
@Transient
|
||||
lateinit var modelIdentifier: Identifier
|
||||
fun bake() {
|
||||
fun bake(manager: ResourceManager) {
|
||||
modelIdentifier = bakeModel(model, layers)
|
||||
overrides.forEach { it.bake() }
|
||||
overrides.forEach { it.bake(manager) }
|
||||
}
|
||||
|
||||
init {
|
||||
@@ -64,23 +69,34 @@ object CustomGlobalArmorOverrides {
|
||||
|
||||
@Transient
|
||||
lateinit var modelIdentifier: Identifier
|
||||
fun bake() {
|
||||
fun bake(manager: ResourceManager) {
|
||||
modelIdentifier = bakeModel(model, layers)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val overrideCache = WeakCache.memoize<ItemStack, Optional<Identifier>>("ArmorOverrides") { stack ->
|
||||
val id = stack.skyBlockId ?: return@memoize Optional.empty()
|
||||
val override = overrides[id.neuItem] ?: return@memoize Optional.empty()
|
||||
for (suboverride in override.overrides) {
|
||||
if (suboverride.predicate.test(stack)) {
|
||||
return@memoize Optional.of(suboverride.modelIdentifier)
|
||||
}
|
||||
}
|
||||
return@memoize Optional.of(override.modelIdentifier)
|
||||
private fun resolveComponent(slot: EquipmentSlot, model: Identifier): EquippableComponent {
|
||||
return EquippableComponent(
|
||||
slot,
|
||||
null,
|
||||
Optional.of(RegistryKey.of(EquipmentAssetKeys.REGISTRY_KEY, model)),
|
||||
Optional.empty(),
|
||||
Optional.empty(), false, false, false
|
||||
)
|
||||
}
|
||||
|
||||
val overrideCache =
|
||||
WeakCache.memoize<ItemStack, EquipmentSlot, Optional<EquippableComponent>>("ArmorOverrides") { stack, slot ->
|
||||
val id = stack.skyBlockId ?: return@memoize Optional.empty()
|
||||
val override = overrides[id.neuItem] ?: return@memoize Optional.empty()
|
||||
for (suboverride in override.overrides) {
|
||||
if (suboverride.predicate.test(stack)) {
|
||||
return@memoize resolveComponent(slot, suboverride.modelIdentifier).intoOptional()
|
||||
}
|
||||
}
|
||||
return@memoize resolveComponent(slot, override.modelIdentifier).intoOptional()
|
||||
}
|
||||
|
||||
var overrides: Map<String, ArmorOverride> = mapOf()
|
||||
private var bakedOverrides: MutableMap<Identifier, EquipmentModel> = mutableMapOf()
|
||||
private val sentinelFirmRunning = AtomicInteger()
|
||||
@@ -131,20 +147,20 @@ object CustomGlobalArmorOverrides {
|
||||
}
|
||||
val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } }
|
||||
.toMap()
|
||||
associatedMap.forEach { it.value.bake(manager) }
|
||||
return associatedMap
|
||||
}
|
||||
|
||||
override fun apply(prepared: Map<String, ArmorOverride>, manager: ResourceManager, profiler: Profiler) {
|
||||
bakedOverrides.clear()
|
||||
prepared.forEach { it.value.bake() }
|
||||
overrides = prepared
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun overrideArmor(itemStack: ItemStack): Optional<Identifier> {
|
||||
return overrideCache.invoke(itemStack)
|
||||
fun overrideArmor(itemStack: ItemStack, slot: EquipmentSlot): Optional<EquippableComponent> {
|
||||
return overrideCache.invoke(itemStack, slot)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@@ -1,18 +1,14 @@
|
||||
@file:UseSerializers(IdentifierSerializer::class, CustomModelOverrideParser.FirmamentRootPredicateSerializer::class)
|
||||
@file:UseSerializers(IdentifierSerializer::class, FirmamentRootPredicateSerializer::class)
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
|
||||
import java.util.Optional
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import org.slf4j.LoggerFactory
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.client.render.item.ItemModels
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.util.ModelIdentifier
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.resource.SinglePreparationResourceReloader
|
||||
import net.minecraft.text.Text
|
||||
@@ -21,15 +17,15 @@ import net.minecraft.util.profiler.Profiler
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.BakeExtraModelsEvent
|
||||
import moe.nea.firmament.events.CustomItemModelEvent
|
||||
import moe.nea.firmament.events.EarlyResourceReloadEvent
|
||||
import moe.nea.firmament.events.FinalizeResourceManagerEvent
|
||||
import moe.nea.firmament.events.ScreenChangeEvent
|
||||
import moe.nea.firmament.events.subscription.SubscriptionOwner
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
import moe.nea.firmament.util.ErrorUtil
|
||||
import moe.nea.firmament.util.IdentifierSerializer
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.collections.WeakCache
|
||||
import moe.nea.firmament.util.intoOptional
|
||||
import moe.nea.firmament.util.json.SingletonSerializableList
|
||||
import moe.nea.firmament.util.runNull
|
||||
|
||||
@@ -91,7 +87,7 @@ object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalText
|
||||
}
|
||||
|
||||
override fun apply(prepared: CustomGuiTextureOverride, manager: ResourceManager?, profiler: Profiler?) {
|
||||
this.guiClassOverrides = prepared
|
||||
guiClassOverrides = prepared
|
||||
}
|
||||
|
||||
val logger = LoggerFactory.getLogger(CustomGlobalTextures::class.java)
|
||||
@@ -100,7 +96,7 @@ object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalText
|
||||
manager.findResources("overrides/item") { it.namespace == "firmskyblock" && it.path.endsWith(".json") }
|
||||
.mapNotNull {
|
||||
Firmament.tryDecodeJsonFromStream<GlobalItemOverride>(it.value.inputStream).getOrElse { ex ->
|
||||
logger.error("Failed to load global item override at ${it.key}", ex)
|
||||
ErrorUtil.softError("Failed to load global item override at ${it.key}", ex)
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -114,12 +110,12 @@ object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalText
|
||||
manager.getResource(Identifier.of(key.namespace, "filters/screen/${key.path}.json"))
|
||||
.getOrNull()
|
||||
?: return@mapNotNull runNull {
|
||||
logger.error("Failed to locate screen filter at $key")
|
||||
ErrorUtil.softError("Failed to locate screen filter at $key")
|
||||
}
|
||||
val screenFilter =
|
||||
Firmament.tryDecodeJsonFromStream<ScreenFilter>(guiClassResource.inputStream)
|
||||
.getOrElse { ex ->
|
||||
logger.error("Failed to load screen filter at $key", ex)
|
||||
ErrorUtil.softError("Failed to load screen filter at $key", ex)
|
||||
return@mapNotNull null
|
||||
}
|
||||
ItemOverrideCollection(screenFilter, it.value.map { it.second })
|
||||
@@ -139,25 +135,19 @@ object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalText
|
||||
.filterTo(mutableSetOf()) { it.screenFilter.title.matches(newTitle) }
|
||||
}
|
||||
|
||||
val overrideCache =
|
||||
WeakCache.memoize<ItemStack, ItemModels, Optional<BakedModel>>("CustomGlobalTextureModelOverrides") { stack, models ->
|
||||
matchingOverrides
|
||||
.firstNotNullOfOrNull {
|
||||
it.overrides
|
||||
.asSequence()
|
||||
.filter { it.predicate.test(stack) }
|
||||
.map { models.getModel(it.model) }
|
||||
.firstOrNull()
|
||||
}
|
||||
.intoOptional()
|
||||
}
|
||||
@Subscribe
|
||||
fun replaceGlobalModel(event: CustomItemModelEvent) {
|
||||
val override = matchingOverrides
|
||||
.firstNotNullOfOrNull {
|
||||
it.overrides
|
||||
.asSequence()
|
||||
.filter { it.predicate.test(event.itemStack) }
|
||||
.map { it.model }
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun replaceGlobalModel(
|
||||
models: ItemModels,
|
||||
stack: ItemStack,
|
||||
): BakedModel? {
|
||||
return overrideCache.invoke(stack, models).getOrNull()
|
||||
if (override != null)
|
||||
event.overrideIfExists(override)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import com.mojang.datafixers.util.Pair
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DataResult
|
||||
import com.mojang.serialization.Decoder
|
||||
import com.mojang.serialization.DynamicOps
|
||||
import com.mojang.serialization.Encoder
|
||||
import net.minecraft.client.render.item.model.ItemModelTypes
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.FinalizeResourceManagerEvent
|
||||
import moe.nea.firmament.features.texturepack.predicates.AndPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.DisplayNamePredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.ExtraAttributesPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.ItemPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.LorePredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.NotPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.OrPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.PetPredicate
|
||||
import moe.nea.firmament.util.json.KJsonOps
|
||||
|
||||
object CustomModelOverrideParser {
|
||||
|
||||
val LEGACY_CODEC: Codec<FirmamentModelPredicate> =
|
||||
Codec.of(
|
||||
Encoder.error("cannot encode legacy firmament model predicates"),
|
||||
object : Decoder<FirmamentModelPredicate> {
|
||||
override fun <T : Any?> decode(
|
||||
ops: DynamicOps<T>,
|
||||
input: T
|
||||
): DataResult<Pair<FirmamentModelPredicate, T>> {
|
||||
try {
|
||||
val pred = Firmament.json.decodeFromJsonElement(
|
||||
FirmamentRootPredicateSerializer,
|
||||
ops.convertTo(KJsonOps.INSTANCE, input))
|
||||
return DataResult.success(Pair.of(pred, ops.empty()))
|
||||
} catch (ex: Exception) {
|
||||
return DataResult.error { "Could not deserialize ${ex.message}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
val predicateParsers = mutableMapOf<Identifier, FirmamentModelPredicateParser>()
|
||||
|
||||
|
||||
fun registerPredicateParser(name: String, parser: FirmamentModelPredicateParser) {
|
||||
predicateParsers[Identifier.of("firmament", name)] = parser
|
||||
}
|
||||
|
||||
init {
|
||||
registerPredicateParser("display_name", DisplayNamePredicate.Parser)
|
||||
registerPredicateParser("lore", LorePredicate.Parser)
|
||||
registerPredicateParser("all", AndPredicate.Parser)
|
||||
registerPredicateParser("any", OrPredicate.Parser)
|
||||
registerPredicateParser("not", NotPredicate.Parser)
|
||||
registerPredicateParser("item", ItemPredicate.Parser)
|
||||
registerPredicateParser("extra_attributes", ExtraAttributesPredicate.Parser)
|
||||
registerPredicateParser("pet", PetPredicate.Parser)
|
||||
}
|
||||
|
||||
private val neverPredicate = listOf(
|
||||
object : FirmamentModelPredicate {
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
fun parsePredicates(predicates: JsonObject): List<FirmamentModelPredicate> {
|
||||
val parsedPredicates = mutableListOf<FirmamentModelPredicate>()
|
||||
for (predicateName in predicates.keySet()) {
|
||||
if (!predicateName.startsWith("firmament:")) continue
|
||||
val identifier = Identifier.of(predicateName)
|
||||
val parser = predicateParsers[identifier] ?: return neverPredicate
|
||||
val parsedPredicate = parser.parse(predicates[predicateName]) ?: return neverPredicate
|
||||
parsedPredicates.add(parsedPredicate)
|
||||
}
|
||||
return parsedPredicates
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseCustomModelOverrides(jsonObject: JsonObject): Array<FirmamentModelPredicate>? {
|
||||
val predicates = (jsonObject["predicate"] as? JsonObject) ?: return null
|
||||
val parsedPredicates = parsePredicates(predicates)
|
||||
if (parsedPredicates.isEmpty())
|
||||
return null
|
||||
return parsedPredicates.toTypedArray()
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun finalizeResources(event: FinalizeResourceManagerEvent) {
|
||||
ItemModelTypes.ID_MAPPER.put(
|
||||
Firmament.identifier("predicates/legacy"),
|
||||
PredicateModel.Unbaked.CODEC
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture
|
||||
import com.mojang.authlib.properties.Property
|
||||
import java.util.Optional
|
||||
import org.jetbrains.annotations.Nullable
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.block.SkullBlock
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.component.type.ProfileComponent
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.CustomItemModelEvent
|
||||
import moe.nea.firmament.events.FinalizeResourceManagerEvent
|
||||
import moe.nea.firmament.events.TickEvent
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
import moe.nea.firmament.features.debug.PowerUserTools
|
||||
import moe.nea.firmament.gui.config.ManagedConfig
|
||||
import moe.nea.firmament.util.collections.WeakCache
|
||||
import moe.nea.firmament.util.mc.decodeProfileTextureProperty
|
||||
import moe.nea.firmament.util.skyBlockId
|
||||
|
||||
object CustomSkyBlockTextures : FirmamentFeature {
|
||||
override val identifier: String
|
||||
get() = "custom-skyblock-textures"
|
||||
|
||||
object TConfig : ManagedConfig(identifier, Category.INTEGRATIONS) { // TODO: should this be its own thing?
|
||||
val enabled by toggle("enabled") { true }
|
||||
val skullsEnabled by toggle("skulls-enabled") { true }
|
||||
val cacheForever by toggle("cache-forever") { true }
|
||||
val cacheDuration by integer("cache-duration", 0, 100) { 1 }
|
||||
val enableModelOverrides by toggle("model-overrides") { true }
|
||||
val enableArmorOverrides by toggle("armor-overrides") { true }
|
||||
val enableBlockOverrides by toggle("block-overrides") { true }
|
||||
val enableLegacyCIT by toggle("legacy-cit") { true }
|
||||
val allowRecoloringUiText by toggle("recolor-text") { true }
|
||||
}
|
||||
|
||||
override val config: ManagedConfig
|
||||
get() = TConfig
|
||||
|
||||
val allItemCaches by lazy {
|
||||
listOf(
|
||||
skullTextureCache.cache,
|
||||
CustomGlobalArmorOverrides.overrideCache.cache
|
||||
)
|
||||
}
|
||||
|
||||
init {
|
||||
PowerUserTools.getSkullId = ::getSkullTexture
|
||||
}
|
||||
|
||||
fun clearAllCaches() {
|
||||
allItemCaches.forEach(WeakCache<*, *, *>::clear)
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onTick(it: TickEvent) {
|
||||
if (TConfig.cacheForever) return
|
||||
if (TConfig.cacheDuration < 1 || it.tickCount % TConfig.cacheDuration == 0) {
|
||||
clearAllCaches()
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onStart(event: FinalizeResourceManagerEvent) {
|
||||
event.registerOnApply("Clear firmament CIT caches") {
|
||||
clearAllCaches()
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onCustomModelId(it: CustomItemModelEvent) {
|
||||
if (!TConfig.enabled) return
|
||||
val id = it.itemStack.skyBlockId ?: return
|
||||
it.overrideIfExists(Identifier.of("firmskyblock", id.identifier.path))
|
||||
}
|
||||
|
||||
private val skullTextureCache =
|
||||
WeakCache.memoize<ProfileComponent, Optional<Identifier>>("SkullTextureCache") { component ->
|
||||
val id = getSkullTexture(component) ?: return@memoize Optional.empty()
|
||||
if (!MinecraftClient.getInstance().resourceManager.getResource(id).isPresent) {
|
||||
return@memoize Optional.empty()
|
||||
}
|
||||
return@memoize Optional.of(id)
|
||||
}
|
||||
|
||||
private val mcUrlRegex = "https?://textures.minecraft.net/texture/([a-fA-F0-9]+)".toRegex()
|
||||
|
||||
fun getSkullId(textureProperty: Property): String? {
|
||||
val texture = decodeProfileTextureProperty(textureProperty) ?: return null
|
||||
val textureUrl =
|
||||
texture.textures[MinecraftProfileTexture.Type.SKIN]?.url ?: return null
|
||||
val mcUrlData = mcUrlRegex.matchEntire(textureUrl) ?: return null
|
||||
return mcUrlData.groupValues[1]
|
||||
}
|
||||
|
||||
fun getSkullTexture(profile: ProfileComponent): Identifier? {
|
||||
val id = getSkullId(profile.properties["textures"].firstOrNull() ?: return null) ?: return null
|
||||
return Identifier.of("firmskyblock", "textures/placedskull/$id.png")
|
||||
}
|
||||
|
||||
fun modifySkullTexture(
|
||||
type: SkullBlock.SkullType?,
|
||||
component: ProfileComponent?,
|
||||
cir: CallbackInfoReturnable<RenderLayer>
|
||||
) {
|
||||
if (type != SkullBlock.Type.PLAYER) return
|
||||
if (!TConfig.skullsEnabled) return
|
||||
if (component == null) return
|
||||
|
||||
val n = skullTextureCache.invoke(component).getOrNull() ?: return
|
||||
cir.returnValue = RenderLayer.getEntityTranslucent(n)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import moe.nea.firmament.features.texturepack.predicates.AndPredicate
|
||||
|
||||
object FirmamentRootPredicateSerializer : KSerializer<FirmamentModelPredicate> {
|
||||
val delegateSerializer = kotlinx.serialization.json.JsonObject.serializer()
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = SerialDescriptor("FirmamentModelRootPredicate", delegateSerializer.descriptor)
|
||||
|
||||
override fun deserialize(decoder: Decoder): FirmamentModelPredicate {
|
||||
val json = decoder.decodeSerializableValue(delegateSerializer).intoGson() as JsonObject
|
||||
return AndPredicate(CustomModelOverrideParser.parsePredicates(json).toTypedArray())
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: FirmamentModelPredicate) {
|
||||
TODO("Cannot serialize firmament predicates")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.client.item.ItemModelManager
|
||||
import net.minecraft.client.render.item.ItemRenderState
|
||||
import net.minecraft.client.render.item.model.ItemModel
|
||||
import net.minecraft.client.render.item.model.ItemModelTypes
|
||||
import net.minecraft.client.render.model.ResolvableModel
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.ModelTransformationMode
|
||||
|
||||
class PredicateModel {
|
||||
data class Baked(
|
||||
val fallback: ItemModel,
|
||||
val overrides: List<Override>
|
||||
) : ItemModel {
|
||||
data class Override(
|
||||
val model: ItemModel,
|
||||
val predicate: FirmamentModelPredicate,
|
||||
)
|
||||
|
||||
override fun update(
|
||||
state: ItemRenderState,
|
||||
stack: ItemStack,
|
||||
resolver: ItemModelManager,
|
||||
transformationMode: ModelTransformationMode,
|
||||
world: ClientWorld?,
|
||||
user: LivingEntity?,
|
||||
seed: Int
|
||||
) {
|
||||
val model =
|
||||
overrides
|
||||
.find { it.predicate.test(stack) }
|
||||
?.model
|
||||
?: fallback
|
||||
model.update(state, stack, resolver, transformationMode, world, user, seed)
|
||||
}
|
||||
}
|
||||
|
||||
data class Unbaked(
|
||||
val fallback: ItemModel.Unbaked,
|
||||
val overrides: List<Override>,
|
||||
) : ItemModel.Unbaked {
|
||||
companion object {
|
||||
val OVERRIDE_CODEC: Codec<Override> = RecordCodecBuilder.create {
|
||||
it.group(
|
||||
ItemModelTypes.CODEC.fieldOf("model").forGetter(Override::model),
|
||||
CustomModelOverrideParser.LEGACY_CODEC.fieldOf("predicate").forGetter(Override::predicate),
|
||||
).apply(it, Unbaked::Override)
|
||||
}
|
||||
val CODEC: MapCodec<Unbaked> =
|
||||
RecordCodecBuilder.mapCodec {
|
||||
it.group(
|
||||
ItemModelTypes.CODEC.fieldOf("fallback").forGetter(Unbaked::fallback),
|
||||
OVERRIDE_CODEC.listOf().fieldOf("overrides").forGetter(Unbaked::overrides),
|
||||
).apply(it, ::Unbaked)
|
||||
}
|
||||
}
|
||||
|
||||
data class Override(
|
||||
val model: ItemModel.Unbaked,
|
||||
val predicate: FirmamentModelPredicate,
|
||||
)
|
||||
|
||||
override fun resolve(resolver: ResolvableModel.Resolver) {
|
||||
fallback.resolve(resolver)
|
||||
overrides.forEach { it.model.resolve(resolver) }
|
||||
}
|
||||
|
||||
override fun getCodec(): MapCodec<out Unbaked> {
|
||||
return CODEC
|
||||
}
|
||||
|
||||
override fun bake(context: ItemModel.BakeContext): ItemModel {
|
||||
return Baked(
|
||||
fallback.bake(context),
|
||||
overrides.map { Baked.Override(it.model.bake(context), it.predicate) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@ interface StringMatcher {
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: StringMatcher) {
|
||||
encoder.encodeSerializableValue(delegateSerializer, Companion.serialize(value).intoKotlinJson())
|
||||
encoder.encodeSerializableValue(delegateSerializer, serialize(value).intoKotlinJson())
|
||||
}
|
||||
|
||||
}
|
||||
@@ -43,7 +43,7 @@ data class TintOverrides(
|
||||
val override = (value as? JsonPrimitive)
|
||||
?.takeIf(JsonPrimitive::isNumber)
|
||||
?.asInt
|
||||
?.let(::Fixed)
|
||||
?.let(TintOverrides::Fixed)
|
||||
if (override == null) {
|
||||
ErrorUtil.softError("Invalid tint override for a layer: $value")
|
||||
continue
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import net.minecraft.client.item.ItemModelManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
@Mixin(ItemModelManager.class)
|
||||
public class ApplyHeadModelInItemRenderer {
|
||||
// TODO: replace head_model with a condition model (if possible, automatically)
|
||||
// TODO: ItemAsset.CODEC should upgrade partials
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.mixins;
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures;
|
||||
import net.minecraft.block.SkullBlock;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.block.entity.SkullBlockEntityRenderer;
|
||||
import net.minecraft.component.type.ProfileComponent;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@@ -14,8 +15,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(SkullBlockEntityRenderer.class)
|
||||
public class CustomSkullTexturePatch {
|
||||
@Inject(method = "getRenderLayer", at = @At("HEAD"), cancellable = true)
|
||||
private static void onGetRenderLayer(SkullBlock.SkullType type, ProfileComponent profile, CallbackInfoReturnable<RenderLayer> cir) {
|
||||
@Inject(
|
||||
method = "getRenderLayer(Lnet/minecraft/block/SkullBlock$SkullType;Lnet/minecraft/component/type/ProfileComponent;Lnet/minecraft/util/Identifier;)Lnet/minecraft/client/render/RenderLayer;",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private static void onGetRenderLayer(SkullBlock.SkullType type, ProfileComponent profile, Identifier texture, CallbackInfoReturnable<RenderLayer> cir) {
|
||||
CustomSkyBlockTextures.INSTANCE.modifySkullTexture(type, profile, cir);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,30 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.features.texturepack.CustomGlobalArmorOverrides;
|
||||
import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer;
|
||||
import net.minecraft.component.ComponentType;
|
||||
import net.minecraft.component.type.EquippableComponent;
|
||||
import net.minecraft.entity.EquipmentSlot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(ArmorFeatureRenderer.class)
|
||||
public class PatchArmorTexture {
|
||||
@WrapOperation(
|
||||
@ModifyExpressionValue(
|
||||
method = "renderArmor",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/component/type/EquippableComponent;model()Ljava/util/Optional;"))
|
||||
private Optional<Identifier> overrideLayers(
|
||||
EquippableComponent instance, Operation<Optional<Identifier>> original, @Local(argsOnly = true) ItemStack itemStack
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/item/ItemStack;get(Lnet/minecraft/component/ComponentType;)Ljava/lang/Object;"))
|
||||
private Object overrideLayers(
|
||||
Object original, @Local(argsOnly = true) ItemStack itemStack, @Local(argsOnly = true) EquipmentSlot slot
|
||||
) {
|
||||
// TODO: check that all armour items are naturally equippable and have the equppable component. otherwise our call here will not be reached.
|
||||
var overrides = CustomGlobalArmorOverrides.overrideArmor(itemStack);
|
||||
return overrides.or(() -> original.call(instance));
|
||||
var overrides = CustomGlobalArmorOverrides.overrideArmor(itemStack, slot);
|
||||
return overrides.orElse((EquippableComponent) original);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import moe.nea.firmament.features.texturepack.CustomGlobalArmorOverrides;
|
||||
import net.minecraft.client.render.entity.equipment.EquipmentModel;
|
||||
import net.minecraft.client.render.entity.equipment.EquipmentModelLoader;
|
||||
import net.minecraft.client.render.entity.equipment.EquipmentRenderer;
|
||||
import net.minecraft.item.equipment.EquipmentAsset;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
// TODO: auto import legacy models, maybe!!! in a later patch tho
|
||||
@Mixin(EquipmentRenderer.class)
|
||||
public class PatchLegacyArmorLayerSupport {
|
||||
@WrapOperation(method = "render(Lnet/minecraft/client/render/entity/equipment/EquipmentModel$LayerType;Lnet/minecraft/registry/RegistryKey;Lnet/minecraft/client/model/Model;Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/util/Identifier;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/equipment/EquipmentModelLoader;get(Lnet/minecraft/registry/RegistryKey;)Lnet/minecraft/client/render/entity/equipment/EquipmentModel;"))
|
||||
private EquipmentModel patchModelLayers(EquipmentModelLoader instance, RegistryKey<EquipmentAsset> assetKey, Operation<EquipmentModel> original) {
|
||||
var modelOverride = CustomGlobalArmorOverrides.overrideArmorLayer(assetKey.getValue());
|
||||
if (modelOverride != null) return modelOverride;
|
||||
return original.call(instance, assetKey);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.events.BakeExtraModelsEvent;
|
||||
import net.minecraft.client.item.ItemAssetsLoader;
|
||||
import net.minecraft.client.render.model.BakedModelManager;
|
||||
import net.minecraft.client.render.model.BlockStatesLoader;
|
||||
import net.minecraft.client.render.model.ItemModel;
|
||||
import net.minecraft.client.render.model.ReferencedModelsCollector;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.util.ModelIdentifier;
|
||||
@@ -13,24 +15,17 @@ import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ReferencedModelsCollector.class)
|
||||
@Mixin(BakedModelManager.class)
|
||||
public abstract class ReferenceCustomModelsPatch {
|
||||
@Shadow
|
||||
protected abstract void addTopLevelModel(ModelIdentifier modelId, UnbakedModel model);
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Map<Identifier, UnbakedModel> inputs;
|
||||
|
||||
@Inject(method = "addBlockStates", at = @At("RETURN"))
|
||||
private void addFirmamentReferencedModels(
|
||||
BlockStatesLoader.BlockStateDefinition definition, CallbackInfo ci
|
||||
) {
|
||||
BakeExtraModelsEvent.Companion.publish(new BakeExtraModelsEvent(
|
||||
(modelIdentifier, identifier) -> addTopLevelModel(modelIdentifier, new ItemModel(identifier))));
|
||||
@Inject(method = "collect", at = @At("RETURN"))
|
||||
private static void addFirmamentReferencedModels(
|
||||
UnbakedModel missingModel, Map<Identifier, UnbakedModel> models, BlockStatesLoader.BlockStateDefinition blockStates, ItemAssetsLoader.Result itemAssets, CallbackInfoReturnable<ReferencedModelsCollector> cir,
|
||||
@Local ReferencedModelsCollector collector) {
|
||||
// TODO: Insert fake models based on firmskyblock models for a smoother transition
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import moe.nea.firmament.events.CustomItemModelEvent;
|
||||
import net.minecraft.client.item.ItemModelManager;
|
||||
import net.minecraft.component.ComponentType;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(ItemModelManager.class)
|
||||
public class ReplaceItemModelPatch {
|
||||
@WrapOperation(
|
||||
method = "update(Lnet/minecraft/client/render/item/ItemRenderState;Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;Lnet/minecraft/world/World;Lnet/minecraft/entity/LivingEntity;I)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;get(Lnet/minecraft/component/ComponentType;)Ljava/lang/Object;"))
|
||||
private Object replaceItemModelByIdentifier(ItemStack instance, ComponentType componentType, Operation<Object> original) {
|
||||
var override = CustomItemModelEvent.getModelIdentifier(instance);
|
||||
if (override != null)
|
||||
return override;
|
||||
return original.call(instance, componentType);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package moe.nea.firmament.mixins;
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
@@ -44,6 +44,7 @@ class SubscribeAnnotationProcessor(
|
||||
appendLine()
|
||||
appendLine("import moe.nea.firmament.events.subscription.*")
|
||||
appendLine()
|
||||
appendLine("@Suppress()")
|
||||
appendLine("class $generatedFileName : SubscriptionList {")
|
||||
appendLine(" override fun provideSubscriptions(addSubscription: (Subscription<*>) -> Unit) {")
|
||||
for (subscription in subscriptions) {
|
||||
|
||||
Reference in New Issue
Block a user