WIP: Port to compilation on 1.21.4

This commit is contained in:
Linnea Gräf
2024-12-07 13:26:03 +01:00
parent cdb5e60f52
commit bf7795df22
77 changed files with 780 additions and 1446 deletions

View File

@@ -25,7 +25,7 @@ plugins {
alias(libs.plugins.kotlin.plugin.ksp) alias(libs.plugins.kotlin.plugin.ksp)
// alias(libs.plugins.loom) // alias(libs.plugins.loom)
// TODO: use arch loom once they update to 1.8 // 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) alias(libs.plugins.shadow)
id("moe.nea.licenseextractificator") id("moe.nea.licenseextractificator")
id("moe.nea.mc-auto-translations") version "0.1.0" 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() val compatSourceSets: MutableSet<SourceSet> = mutableSetOf()
fun createIsolatedSourceSet(name: String, path: String = "compat/$name", isEnabled: Boolean = true): SourceSet { fun createIsolatedSourceSet(name: String, path: String = "compat/$name", isEnabled: Boolean = true): SourceSet {
val ss = sourceSets.create(name) { val ss = sourceSets.create(name) {
if (isEnabled) {
this.java.setSrcDirs(listOf(layout.projectDirectory.dir("src/$path/java"))) this.java.setSrcDirs(listOf(layout.projectDirectory.dir("src/$path/java")))
this.kotlin.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>())
}
} }
val mainSS = sourceSets.main.get() val mainSS = sourceSets.main.get()
val upperName = ss.name.capitalizeN() val upperName = ss.name.capitalizeN()
@@ -221,15 +216,16 @@ val testAgent by configurations.creating {
val configuredSourceSet = createIsolatedSourceSet("configured", val configuredSourceSet = createIsolatedSourceSet("configured",
isEnabled = false) // Wait for update (also low prio, because configured sucks) 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 citResewnSourceSet = createIsolatedSourceSet("citresewn", isEnabled = false) // TODO: Wait for update
val yaclSourceSet = createIsolatedSourceSet("yacl") val yaclSourceSet = createIsolatedSourceSet("yacl", isEnabled = false)
val explosiveEnhancementSourceSet = val explosiveEnhancementSourceSet =
createIsolatedSourceSet("explosiveEnhancement", isEnabled = false) // TODO: wait for their port createIsolatedSourceSet("explosiveEnhancement", isEnabled = false) // TODO: wait for their port
val wildfireGenderSourceSet = createIsolatedSourceSet("wildfireGender", isEnabled = false) // TODO: wait on their port val wildfireGenderSourceSet = createIsolatedSourceSet("wildfireGender", isEnabled = false) // TODO: wait on their port
val modmenuSourceSet = createIsolatedSourceSet("modmenu") val modmenuSourceSet = createIsolatedSourceSet("modmenu", isEnabled = false)
val reiSourceSet = createIsolatedSourceSet("rei") val reiSourceSet = createIsolatedSourceSet("rei", isEnabled = false)
val moulconfigSourceSet = createIsolatedSourceSet("moulconfig") val moulconfigSourceSet = createIsolatedSourceSet("moulconfig", isEnabled = false)
val customTexturesSourceSet = createIsolatedSourceSet("texturePacks", "texturePacks")
dependencies { dependencies {
// Minecraft dependencies // Minecraft dependencies

View File

@@ -3,7 +3,7 @@
// SPDX-License-Identifier: CC0-1.0 // SPDX-License-Identifier: CC0-1.0
plugins { plugins {
kotlin("jvm") version "1.8.10" kotlin("jvm") version "2.1.0"
`kotlin-dsl` `kotlin-dsl`
} }
repositories { repositories {

View File

@@ -3,21 +3,23 @@
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
[versions] [versions]
minecraft = "1.21.3" minecraft = "1.21.4"
# Update from https://kotlinlang.org/ # Update from https://kotlinlang.org/
kotlin = "2.0.21" kotlin = "2.1.0"
# Update from https://github.com/google/ksp/releases # 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 # Update from https://linkie.shedaniel.me/dependencies?loader=fabric
fabric_loader = "0.16.9" fabric_loader = "0.16.9"
fabric_api = "0.107.0+1.21.3" fabric_api = "0.110.5+1.21.4"
fabric_kotlin = "1.12.3+kotlin.2.0.21" yarn = "1.21.4+build.1"
yarn = "1.21.3+build.2" modmenu = "13.0.0-beta.1"
architectury = "15.0.1"
rei = "17.0.789" 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/ # Update from https://maven.architectury.dev/dev/architectury/loom/dev.architectury.loom.gradle.plugin/
loom = "1.7.414" loom = "1.7.414"
@@ -26,16 +28,16 @@ loom = "1.7.414"
qolify = "1.6.0-1.21.1" qolify = "1.6.0-1.21.1"
# Update from https://modrinth.com/mod/sodium/versions?l=fabric # 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 # 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 # Update from https://modrinth.com/mod/no-chat-reports/versions?l=fabric
ncr = "Fabric-1.21.3-v2.10.0" ncr = "Fabric-1.21.3-v2.10.0"
# Update from https://modrinth.com/mod/female-gender/versions?l=fabric # 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 # Update from https://modrinth.com/mod/explosive-enhancement/versions?l=fabric
explosiveenhancement = "1.2.3-1.21.0" explosiveenhancement = "1.2.3-1.21.0"
@@ -49,12 +51,13 @@ citresewn = "1.2.0+1.21"
devauth = "1.2.0" devauth = "1.2.0"
# Update from https://ktor.io/ # Update from https://ktor.io/
ktor = "3.0.1" ktor = "3.0.2"
# Update from https://repo.nea.moe/#/releases/moe/nea/neurepoparser # Update from https://repo.nea.moe/#/releases/moe/nea/neurepoparser
neurepoparser = "1.6.0" neurepoparser = "1.6.0"
# Update from https://github.com/HotswapProjects/HotswapAgent/releases # Update from https://github.com/HotswapProjects/HotswapAgent/releases
# TODO: bump to 2.0.1
hotswap_agent = "1.4.2-SNAPSHOT" hotswap_agent = "1.4.2-SNAPSHOT"
# Update from https://github.com/LlamaLad7/MixinExtras/tags # Update from https://github.com/LlamaLad7/MixinExtras/tags

View File

@@ -4,6 +4,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -59,6 +59,14 @@ public class InitReplacer extends TreeScanner<Void, Void> {
var target = plugin.utils.getAnnotationValue(jcAnnotation, "value"); var target = plugin.utils.getAnnotationValue(jcAnnotation, "value");
var targetClass = plugin.utils.resolveClassLiteralExpression(target).tsym.flatName().toString(); var targetClass = plugin.utils.resolveClassLiteralExpression(target).tsym.flatName().toString();
var intermediaryClass = mappingTree.resolveClassToIntermediary(targetClass); 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 remapper = treeMaker.Select(treeMaker.This(classTree.type), names.fromString("remapper"));
var remappingCall = treeMaker.Apply( var remappingCall = treeMaker.Apply(
List.nil(), List.nil(),

View File

@@ -57,7 +57,12 @@ public class IntermediaryMethodReplacer extends TreeScanner<Void, Void> {
} }
var head = node.typeargs.head; var head = node.typeargs.head;
var resolved = plugin.utils.resolveClassName(head, compilationUnit); 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"); fieldAccess.name = plugin.names.fromString("id");
node.typeargs = List.nil(); node.typeargs = List.nil();
node.args = List.of(plugin.treeMaker.Literal(mappedName)); node.args = List.of(plugin.treeMaker.Literal(mappedName));

View File

@@ -41,8 +41,11 @@ public class MappingTree {
} }
public String resolveClassToIntermediary(String className) { public String resolveClassToIntermediary(String className) {
return classLookup.get(className.replace(".", "/")) var cls = classLookup.get(className.replace(".", "/"));
.getClassNames().get(targetIndex) if (cls == null) {
return null;
}
return cls.getClassNames().get(targetIndex)
.replace("/", "."); .replace("/", ".");
} }
} }

View File

@@ -111,7 +111,7 @@ public class Utils {
var error = diagnostics.error( var error = diagnostics.error(
JCDiagnostic.DiagnosticFlag.API, JCDiagnostic.DiagnosticFlag.API,
log.currentSource(), log.currentSource(),
((JCTree) node).pos(), node == null ? null : ((JCTree) node).pos(),
"firmament.generic", "firmament.generic",
message message
); );

View File

@@ -7,6 +7,6 @@ public class EarlyRiser implements Runnable {
new ClientPlayerRiser().addTinkerers(); new ClientPlayerRiser().addTinkerers();
new HandledScreenRiser().addTinkerers(); new HandledScreenRiser().addTinkerers();
new SectionBuilderRiser().addTinkerers(); new SectionBuilderRiser().addTinkerers();
new ItemColorsSodiumRiser().addTinkerers(); // TODO: new ItemColorsSodiumRiser().addTinkerers();
} }
} }

View File

@@ -3,7 +3,6 @@ package moe.nea.firmament.init;
import me.shedaniel.mm.api.ClassTinkerers; import me.shedaniel.mm.api.ClassTinkerers;
import net.minecraft.client.gui.Element; import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.ParentElement;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type; import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -18,7 +18,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ClientPlayNetworkHandler.class) @Mixin(ClientPlayNetworkHandler.class)
public abstract class IncomingPacketListenerPatches { public abstract class IncomingPacketListenerPatches {
@ModifyExpressionValue(method = "onCommandTree", at = @At(value = "NEW", target = "(Lcom/mojang/brigadier/tree/RootCommandNode;)Lcom/mojang/brigadier/CommandDispatcher;", remap = false)) @ModifyExpressionValue(method = "onCommandTree", at = @At(value = "NEW", target = "(Lcom/mojang/brigadier/tree/RootCommandNode;)Lcom/mojang/brigadier/CommandDispatcher;", remap = false))
public CommandDispatcher onOnCommandTree(CommandDispatcher dispatcher) { public CommandDispatcher onOnCommandTree(CommandDispatcher dispatcher) {
MaskCommands.Companion.publish(new MaskCommands(dispatcher)); MaskCommands.Companion.publish(new MaskCommands(dispatcher));
@@ -31,7 +30,7 @@ public abstract class IncomingPacketListenerPatches {
packet.getParameters(), packet.getParameters(),
new Vec3d(packet.getX(), packet.getY(), packet.getZ()), new Vec3d(packet.getX(), packet.getY(), packet.getZ()),
new Vector3f(packet.getOffsetX(), packet.getOffsetY(), packet.getOffsetZ()), new Vector3f(packet.getOffsetX(), packet.getOffsetY(), packet.getOffsetZ()),
packet.isLongDistance(), packet.isImportant(),
packet.getCount(), packet.getCount(),
packet.getSpeed() packet.getSpeed()
); );

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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"));
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -1,11 +1,13 @@
package moe.nea.firmament.events package moe.nea.firmament.events
import java.util.function.BiConsumer import java.util.function.BiConsumer
import net.minecraft.client.item.ItemAssetsLoader
import net.minecraft.client.render.model.ReferencedModelsCollector import net.minecraft.client.render.model.ReferencedModelsCollector
import net.minecraft.client.util.ModelIdentifier import net.minecraft.client.util.ModelIdentifier
import net.minecraft.util.Identifier 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( class BakeExtraModelsEvent(
private val addAnyModel: BiConsumer<ModelIdentifier, Identifier>, private val addAnyModel: BiConsumer<ModelIdentifier, Identifier>,
) : FirmamentEvent() { ) : FirmamentEvent() {
@@ -15,10 +17,13 @@ class BakeExtraModelsEvent(
} }
fun addItemModel(modelIdentifier: ModelIdentifier) { fun addItemModel(modelIdentifier: ModelIdentifier) {
addNonItemModel( // TODO: If this is still needed: ItemAssetsLoader.FINDER
modelIdentifier, // addNonItemModel(
modelIdentifier.id.withPrefixedPath(ReferencedModelsCollector.ITEM_DIRECTORY)) // 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>() companion object : FirmamentEventBus<BakeExtraModelsEvent>()
} }

View File

@@ -1,38 +1,23 @@
package moe.nea.firmament.events 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 net.minecraft.item.ItemStack
import moe.nea.firmament.util.ErrorUtil import net.minecraft.util.Identifier
import moe.nea.firmament.util.collections.WeakCache
// TODO: assert an order on these events
data class CustomItemModelEvent( data class CustomItemModelEvent(
val itemStack: ItemStack, val itemStack: ItemStack,
var overrideModel: ModelIdentifier? = null, var overrideModel: Identifier? = null,
) : FirmamentEvent() { ) : FirmamentEvent() {
companion object : FirmamentEventBus<CustomItemModelEvent>() { 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 @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 if (itemStack == null) return null
return publish(CustomItemModelEvent(itemStack)).overrideModel 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()
} }
} }

View File

@@ -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.inventory.storageoverlay.StorageOverlay
import moe.nea.firmament.features.mining.PickaxeAbility import moe.nea.firmament.features.mining.PickaxeAbility
import moe.nea.firmament.features.mining.PristineProfitTracker 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.FairySouls
import moe.nea.firmament.features.world.Waypoints import moe.nea.firmament.features.world.Waypoints
import moe.nea.firmament.util.data.DataHolder import moe.nea.firmament.util.data.DataHolder
@@ -70,7 +69,6 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
loadFeature(QuickCommands) loadFeature(QuickCommands)
loadFeature(PetFeatures) loadFeature(PetFeatures)
loadFeature(SaveCursorPosition) loadFeature(SaveCursorPosition)
loadFeature(CustomSkyBlockTextures)
loadFeature(PriceData) loadFeature(PriceData)
loadFeature(Fixes) loadFeature(Fixes)
loadFeature(DianaWaypoints) loadFeature(DianaWaypoints)

View File

@@ -1,5 +1,3 @@
package moe.nea.firmament.features.chat package moe.nea.firmament.features.chat
import io.ktor.client.request.get 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 io.ktor.utils.io.jvm.javaio.toInputStream
import java.net.URL import java.net.URL
import java.util.Collections import java.util.Collections
import java.util.concurrent.atomic.AtomicInteger
import moe.nea.jarvis.api.Point import moe.nea.jarvis.api.Point
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlin.math.min import kotlin.math.min
import net.minecraft.client.gui.screen.ChatScreen import net.minecraft.client.gui.screen.ChatScreen
import net.minecraft.client.render.RenderLayer
import net.minecraft.client.texture.NativeImage import net.minecraft.client.texture.NativeImage
import net.minecraft.client.texture.NativeImageBackedTexture import net.minecraft.client.texture.NativeImageBackedTexture
import net.minecraft.scoreboard.ScoreboardCriterion.RenderType
import net.minecraft.text.ClickEvent import net.minecraft.text.ClickEvent
import net.minecraft.text.HoverEvent import net.minecraft.text.HoverEvent
import net.minecraft.text.Style import net.minecraft.text.Style
@@ -54,6 +51,7 @@ object ChatLinks : FirmamentFeature {
override val config get() = TConfig override val config get() = TConfig
val urlRegex = "https://[^. ]+\\.[^ ]+(\\.?( |$))".toRegex() val urlRegex = "https://[^. ]+\\.[^ ]+(\\.?( |$))".toRegex()
val nextTexId = AtomicInteger(0)
data class Image( data class Image(
val texture: Identifier, val texture: Identifier,
@@ -77,11 +75,12 @@ object ChatLinks : FirmamentFeature {
if (response.status.value == 200) { if (response.status.value == 200) {
val inputStream = response.bodyAsChannel().toInputStream(Firmament.globalJob) val inputStream = response.bodyAsChannel().toInputStream(Firmament.globalJob)
val image = NativeImage.read(inputStream) val image = NativeImage.read(inputStream)
val texture = MC.textureManager.registerDynamicTexture( val texId = Firmament.identifier("dynamic_image_preview${nextTexId.getAndIncrement()}")
"dynamic_image_preview", MC.textureManager.registerTexture(
texId,
NativeImageBackedTexture(image) NativeImageBackedTexture(image)
) )
Image(texture, image.width, image.height) Image(texId, image.width, image.height)
} else } else
null null
} catch (exc: Exception) { } catch (exc: Exception) {

View File

@@ -5,6 +5,7 @@ import kotlin.jvm.optionals.getOrNull
import net.minecraft.block.SkullBlock import net.minecraft.block.SkullBlock
import net.minecraft.block.entity.SkullBlockEntity import net.minecraft.block.entity.SkullBlockEntity
import net.minecraft.component.DataComponentTypes import net.minecraft.component.DataComponentTypes
import net.minecraft.component.type.ProfileComponent
import net.minecraft.entity.Entity import net.minecraft.entity.Entity
import net.minecraft.entity.LivingEntity import net.minecraft.entity.LivingEntity
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
@@ -12,6 +13,7 @@ import net.minecraft.item.Items
import net.minecraft.nbt.NbtOps import net.minecraft.nbt.NbtOps
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.text.TextCodecs import net.minecraft.text.TextCodecs
import net.minecraft.util.Identifier
import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.hit.EntityHitResult import net.minecraft.util.hit.EntityHitResult
import net.minecraft.util.hit.HitResult 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.TickEvent
import moe.nea.firmament.events.WorldKeyboardEvent import moe.nea.firmament.events.WorldKeyboardEvent
import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures
import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.mixins.accessor.AccessorHandledScreen import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
import moe.nea.firmament.util.ClipboardUtils 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 @Subscribe
fun copyInventoryInfo(it: HandledScreenKeyPressedEvent) { fun copyInventoryInfo(it: HandledScreenKeyPressedEvent) {
@@ -116,7 +119,7 @@ object PowerUserTools : FirmamentFeature {
lastCopiedStack = lastCopiedStack =
Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skyblockid", sbId.neuItem)) Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skyblockid", sbId.neuItem))
} else if (it.matches(TConfig.copyTexturePackId)) { } 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) { if (model == null) {
lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.modelid.fail")) lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.modelid.fail"))
return return
@@ -146,7 +149,7 @@ object PowerUserTools : FirmamentFeature {
lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-profile")) lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-profile"))
return return
} }
val skullTexture = CustomSkyBlockTextures.getSkullTexture(profile) val skullTexture = getSkullId(profile)
if (skullTexture == null) { if (skullTexture == null) {
lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-texture")) lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-texture"))
return return
@@ -179,7 +182,7 @@ object PowerUserTools : FirmamentFeature {
MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail")) MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail"))
return return
} }
val id = CustomSkyBlockTextures.getSkullTexture(entity.owner!!) val id = getSkullId(entity.owner!!)
if (id == null) { if (id == null) {
MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail")) MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail"))
} else { } else {

View File

@@ -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?)
}

View File

@@ -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
}
}

View File

@@ -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()
}
}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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>?)
}

View File

@@ -8,6 +8,7 @@ import net.minecraft.block.BlockState
import net.minecraft.client.gui.screen.world.SelectWorldScreen import net.minecraft.client.gui.screen.world.SelectWorldScreen
import net.minecraft.component.type.MapIdComponent import net.minecraft.component.type.MapIdComponent
import net.minecraft.entity.Entity import net.minecraft.entity.Entity
import net.minecraft.entity.boss.dragon.EnderDragonPart
import net.minecraft.entity.damage.DamageSource import net.minecraft.entity.damage.DamageSource
import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerEntity
import net.minecraft.fluid.Fluid import net.minecraft.fluid.Fluid
@@ -262,6 +263,10 @@ class FakeWorld(
return null return null
} }
override fun getEnderDragonParts(): MutableCollection<EnderDragonPart> {
return mutableListOf()
}
override fun getTickManager(): TickManager { override fun getTickManager(): TickManager {
return TickManager() return TickManager()
} }

View File

@@ -1,4 +1,3 @@
package moe.nea.firmament.repo package moe.nea.firmament.repo
import java.io.InputStream import java.io.InputStream
@@ -21,7 +20,7 @@ import net.minecraft.resource.ResourcePackInfo
import net.minecraft.resource.ResourcePackSource import net.minecraft.resource.ResourcePackSource
import net.minecraft.resource.ResourceType import net.minecraft.resource.ResourceType
import net.minecraft.resource.metadata.ResourceMetadata 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.text.Text
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.PathUtil import net.minecraft.util.PathUtil
@@ -98,9 +97,9 @@ class RepoModResourcePack(val basePath: Path) : ModResourcePack {
return setOf("neurepo") return setOf("neurepo")
} }
override fun <T> parseMetadata(metaReader: ResourceMetadataReader<T>): T? { override fun <T : Any?> parseMetadata(metadataSerializer: ResourceMetadataSerializer<T>?): T? {
return AbstractFileResourcePack.parseMetadata( return AbstractFileResourcePack.parseMetadata(
metaReader, """ metadataSerializer, """
{ {
"pack": { "pack": {
"pack_format": 12, "pack_format": 12,
@@ -111,6 +110,7 @@ class RepoModResourcePack(val basePath: Path) : ModResourcePack {
) )
} }
override fun getInfo(): ResourcePackInfo { override fun getInfo(): ResourcePackInfo {
return ResourcePackInfo("neurepo", Text.literal("NEU Repo"), ResourcePackSource.BUILTIN, Optional.empty()) return ResourcePackInfo("neurepo", Text.literal("NEU Repo"), ResourcePackSource.BUILTIN, Optional.empty())
} }

View File

@@ -4,12 +4,6 @@ accessible class net/minecraft/client/render/RenderLayer$MultiPhaseParameters
accessible class net/minecraft/client/font/TextRenderer$Drawer 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/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 field net/minecraft/entity/mob/CreeperEntity CHARGED Lnet/minecraft/entity/data/TrackedData;
accessible method net/minecraft/entity/decoration/ArmorStandEntity setSmall (Z)V accessible method net/minecraft/entity/decoration/ArmorStandEntity setSmall (Z)V
accessible field net/minecraft/entity/passive/AbstractHorseEntity items Lnet/minecraft/inventory/SimpleInventory; 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/entity/player/PlayerEntity PLAYER_MODEL_PARTS Lnet/minecraft/entity/data/TrackedData;
accessible field net/minecraft/client/render/WorldRenderer chunks Lnet/minecraft/client/render/BuiltChunkStorage; 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;

View File

@@ -8,8 +8,12 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
import kotlinx.serialization.UseSerializers 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.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.ResourceManager
import net.minecraft.resource.SinglePreparationResourceReloader import net.minecraft.resource.SinglePreparationResourceReloader
import net.minecraft.util.Identifier 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.features.texturepack.CustomGlobalTextures.logger
import moe.nea.firmament.util.IdentifierSerializer import moe.nea.firmament.util.IdentifierSerializer
import moe.nea.firmament.util.collections.WeakCache import moe.nea.firmament.util.collections.WeakCache
import moe.nea.firmament.util.intoOptional
import moe.nea.firmament.util.skyBlockId import moe.nea.firmament.util.skyBlockId
object CustomGlobalArmorOverrides { object CustomGlobalArmorOverrides {
@@ -33,9 +38,9 @@ object CustomGlobalArmorOverrides {
) { ) {
@Transient @Transient
lateinit var modelIdentifier: Identifier lateinit var modelIdentifier: Identifier
fun bake() { fun bake(manager: ResourceManager) {
modelIdentifier = bakeModel(model, layers) modelIdentifier = bakeModel(model, layers)
overrides.forEach { it.bake() } overrides.forEach { it.bake(manager) }
} }
init { init {
@@ -64,21 +69,32 @@ object CustomGlobalArmorOverrides {
@Transient @Transient
lateinit var modelIdentifier: Identifier lateinit var modelIdentifier: Identifier
fun bake() { fun bake(manager: ResourceManager) {
modelIdentifier = bakeModel(model, layers) modelIdentifier = bakeModel(model, layers)
} }
} }
val overrideCache = WeakCache.memoize<ItemStack, Optional<Identifier>>("ArmorOverrides") { stack -> 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 id = stack.skyBlockId ?: return@memoize Optional.empty()
val override = overrides[id.neuItem] ?: return@memoize Optional.empty() val override = overrides[id.neuItem] ?: return@memoize Optional.empty()
for (suboverride in override.overrides) { for (suboverride in override.overrides) {
if (suboverride.predicate.test(stack)) { if (suboverride.predicate.test(stack)) {
return@memoize Optional.of(suboverride.modelIdentifier) return@memoize resolveComponent(slot, suboverride.modelIdentifier).intoOptional()
} }
} }
return@memoize Optional.of(override.modelIdentifier) return@memoize resolveComponent(slot, override.modelIdentifier).intoOptional()
} }
var overrides: Map<String, ArmorOverride> = mapOf() var overrides: Map<String, ArmorOverride> = mapOf()
@@ -131,20 +147,20 @@ object CustomGlobalArmorOverrides {
} }
val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } } val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } }
.toMap() .toMap()
associatedMap.forEach { it.value.bake(manager) }
return associatedMap return associatedMap
} }
override fun apply(prepared: Map<String, ArmorOverride>, manager: ResourceManager, profiler: Profiler) { override fun apply(prepared: Map<String, ArmorOverride>, manager: ResourceManager, profiler: Profiler) {
bakedOverrides.clear() bakedOverrides.clear()
prepared.forEach { it.value.bake() }
overrides = prepared overrides = prepared
} }
}) })
} }
@JvmStatic @JvmStatic
fun overrideArmor(itemStack: ItemStack): Optional<Identifier> { fun overrideArmor(itemStack: ItemStack, slot: EquipmentSlot): Optional<EquippableComponent> {
return overrideCache.invoke(itemStack) return overrideCache.invoke(itemStack, slot)
} }
@JvmStatic @JvmStatic

View File

@@ -1,18 +1,14 @@
@file:UseSerializers(IdentifierSerializer::class, CustomModelOverrideParser.FirmamentRootPredicateSerializer::class) @file:UseSerializers(IdentifierSerializer::class, FirmamentRootPredicateSerializer::class)
package moe.nea.firmament.features.texturepack package moe.nea.firmament.features.texturepack
import java.util.Optional
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
import kotlin.jvm.optionals.getOrNull 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.client.util.ModelIdentifier
import net.minecraft.item.ItemStack
import net.minecraft.resource.ResourceManager import net.minecraft.resource.ResourceManager
import net.minecraft.resource.SinglePreparationResourceReloader import net.minecraft.resource.SinglePreparationResourceReloader
import net.minecraft.text.Text import net.minecraft.text.Text
@@ -21,15 +17,15 @@ import net.minecraft.util.profiler.Profiler
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.BakeExtraModelsEvent import moe.nea.firmament.events.BakeExtraModelsEvent
import moe.nea.firmament.events.CustomItemModelEvent
import moe.nea.firmament.events.EarlyResourceReloadEvent import moe.nea.firmament.events.EarlyResourceReloadEvent
import moe.nea.firmament.events.FinalizeResourceManagerEvent import moe.nea.firmament.events.FinalizeResourceManagerEvent
import moe.nea.firmament.events.ScreenChangeEvent import moe.nea.firmament.events.ScreenChangeEvent
import moe.nea.firmament.events.subscription.SubscriptionOwner import moe.nea.firmament.events.subscription.SubscriptionOwner
import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.util.ErrorUtil
import moe.nea.firmament.util.IdentifierSerializer import moe.nea.firmament.util.IdentifierSerializer
import moe.nea.firmament.util.MC 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.json.SingletonSerializableList
import moe.nea.firmament.util.runNull import moe.nea.firmament.util.runNull
@@ -91,7 +87,7 @@ object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalText
} }
override fun apply(prepared: CustomGuiTextureOverride, manager: ResourceManager?, profiler: Profiler?) { override fun apply(prepared: CustomGuiTextureOverride, manager: ResourceManager?, profiler: Profiler?) {
this.guiClassOverrides = prepared guiClassOverrides = prepared
} }
val logger = LoggerFactory.getLogger(CustomGlobalTextures::class.java) 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") } manager.findResources("overrides/item") { it.namespace == "firmskyblock" && it.path.endsWith(".json") }
.mapNotNull { .mapNotNull {
Firmament.tryDecodeJsonFromStream<GlobalItemOverride>(it.value.inputStream).getOrElse { ex -> 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 null
} }
} }
@@ -114,12 +110,12 @@ object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalText
manager.getResource(Identifier.of(key.namespace, "filters/screen/${key.path}.json")) manager.getResource(Identifier.of(key.namespace, "filters/screen/${key.path}.json"))
.getOrNull() .getOrNull()
?: return@mapNotNull runNull { ?: return@mapNotNull runNull {
logger.error("Failed to locate screen filter at $key") ErrorUtil.softError("Failed to locate screen filter at $key")
} }
val screenFilter = val screenFilter =
Firmament.tryDecodeJsonFromStream<ScreenFilter>(guiClassResource.inputStream) Firmament.tryDecodeJsonFromStream<ScreenFilter>(guiClassResource.inputStream)
.getOrElse { ex -> .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 return@mapNotNull null
} }
ItemOverrideCollection(screenFilter, it.value.map { it.second }) ItemOverrideCollection(screenFilter, it.value.map { it.second })
@@ -139,25 +135,19 @@ object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalText
.filterTo(mutableSetOf()) { it.screenFilter.title.matches(newTitle) } .filterTo(mutableSetOf()) { it.screenFilter.title.matches(newTitle) }
} }
val overrideCache = @Subscribe
WeakCache.memoize<ItemStack, ItemModels, Optional<BakedModel>>("CustomGlobalTextureModelOverrides") { stack, models -> fun replaceGlobalModel(event: CustomItemModelEvent) {
matchingOverrides val override = matchingOverrides
.firstNotNullOfOrNull { .firstNotNullOfOrNull {
it.overrides it.overrides
.asSequence() .asSequence()
.filter { it.predicate.test(stack) } .filter { it.predicate.test(event.itemStack) }
.map { models.getModel(it.model) } .map { it.model }
.firstOrNull() .firstOrNull()
} }
.intoOptional()
}
@JvmStatic if (override != null)
fun replaceGlobalModel( event.overrideIfExists(override)
models: ItemModels,
stack: ItemStack,
): BakedModel? {
return overrideCache.invoke(stack, models).getOrNull()
} }

View File

@@ -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
)
}
}

View File

@@ -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)
}
}

View File

@@ -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")
}
}

View File

@@ -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) }
)
}
}
}

View File

@@ -70,7 +70,7 @@ interface StringMatcher {
} }
override fun serialize(encoder: Encoder, value: StringMatcher) { override fun serialize(encoder: Encoder, value: StringMatcher) {
encoder.encodeSerializableValue(delegateSerializer, Companion.serialize(value).intoKotlinJson()) encoder.encodeSerializableValue(delegateSerializer, serialize(value).intoKotlinJson())
} }
} }

View File

@@ -43,7 +43,7 @@ data class TintOverrides(
val override = (value as? JsonPrimitive) val override = (value as? JsonPrimitive)
?.takeIf(JsonPrimitive::isNumber) ?.takeIf(JsonPrimitive::isNumber)
?.asInt ?.asInt
?.let(::Fixed) ?.let(TintOverrides::Fixed)
if (override == null) { if (override == null) {
ErrorUtil.softError("Invalid tint override for a layer: $value") ErrorUtil.softError("Invalid tint override for a layer: $value")
continue continue

View File

@@ -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
}

View File

@@ -1,12 +1,13 @@
package moe.nea.firmament.mixins; package moe.nea.firmament.mixins.custommodels;
import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures; import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures;
import net.minecraft.block.SkullBlock; import net.minecraft.block.SkullBlock;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.block.entity.SkullBlockEntityRenderer; import net.minecraft.client.render.block.entity.SkullBlockEntityRenderer;
import net.minecraft.component.type.ProfileComponent; import net.minecraft.component.type.ProfileComponent;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@@ -14,8 +15,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(SkullBlockEntityRenderer.class) @Mixin(SkullBlockEntityRenderer.class)
public class CustomSkullTexturePatch { public class CustomSkullTexturePatch {
@Inject(method = "getRenderLayer", at = @At("HEAD"), cancellable = true) @Inject(
private static void onGetRenderLayer(SkullBlock.SkullType type, ProfileComponent profile, CallbackInfoReturnable<RenderLayer> cir) { 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); CustomSkyBlockTextures.INSTANCE.modifySkullTexture(type, profile, cir);
} }
} }

View File

@@ -1,29 +1,30 @@
package moe.nea.firmament.mixins.custommodels; 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.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import moe.nea.firmament.features.texturepack.CustomGlobalArmorOverrides; import moe.nea.firmament.features.texturepack.CustomGlobalArmorOverrides;
import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer;
import net.minecraft.component.ComponentType;
import net.minecraft.component.type.EquippableComponent; import net.minecraft.component.type.EquippableComponent;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import java.util.Optional;
@Mixin(ArmorFeatureRenderer.class) @Mixin(ArmorFeatureRenderer.class)
public class PatchArmorTexture { public class PatchArmorTexture {
@WrapOperation( @ModifyExpressionValue(
method = "renderArmor", method = "renderArmor",
at = @At(value = "INVOKE", target = "Lnet/minecraft/component/type/EquippableComponent;model()Ljava/util/Optional;")) at = @At(
private Optional<Identifier> overrideLayers( value = "INVOKE",
EquippableComponent instance, Operation<Optional<Identifier>> original, @Local(argsOnly = true) ItemStack itemStack 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, slot);
var overrides = CustomGlobalArmorOverrides.overrideArmor(itemStack); return overrides.orElse((EquippableComponent) original);
return overrides.or(() -> original.call(instance));
} }
} }

View File

@@ -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);
}
}

View File

@@ -1,8 +1,10 @@
package moe.nea.firmament.mixins.custommodels; package moe.nea.firmament.mixins.custommodels;
import com.llamalad7.mixinextras.sugar.Local;
import moe.nea.firmament.events.BakeExtraModelsEvent; 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.BlockStatesLoader;
import net.minecraft.client.render.model.ItemModel;
import net.minecraft.client.render.model.ReferencedModelsCollector; import net.minecraft.client.render.model.ReferencedModelsCollector;
import net.minecraft.client.render.model.UnbakedModel; import net.minecraft.client.render.model.UnbakedModel;
import net.minecraft.client.util.ModelIdentifier; 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.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Map; import java.util.Map;
@Mixin(ReferencedModelsCollector.class) @Mixin(BakedModelManager.class)
public abstract class ReferenceCustomModelsPatch { public abstract class ReferenceCustomModelsPatch {
@Shadow @Inject(method = "collect", at = @At("RETURN"))
protected abstract void addTopLevelModel(ModelIdentifier modelId, UnbakedModel model); private static void addFirmamentReferencedModels(
UnbakedModel missingModel, Map<Identifier, UnbakedModel> models, BlockStatesLoader.BlockStateDefinition blockStates, ItemAssetsLoader.Result itemAssets, CallbackInfoReturnable<ReferencedModelsCollector> cir,
@Shadow @Local ReferencedModelsCollector collector) {
@Final // TODO: Insert fake models based on firmskyblock models for a smoother transition
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))));
} }
} }

View File

@@ -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);
}
}

View File

@@ -1,4 +1,4 @@
package moe.nea.firmament.mixins; package moe.nea.firmament.mixins.custommodels;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.Operation;

View File

@@ -44,6 +44,7 @@ class SubscribeAnnotationProcessor(
appendLine() appendLine()
appendLine("import moe.nea.firmament.events.subscription.*") appendLine("import moe.nea.firmament.events.subscription.*")
appendLine() appendLine()
appendLine("@Suppress()")
appendLine("class $generatedFileName : SubscriptionList {") appendLine("class $generatedFileName : SubscriptionList {")
appendLine(" override fun provideSubscriptions(addSubscription: (Subscription<*>) -> Unit) {") appendLine(" override fun provideSubscriptions(addSubscription: (Subscription<*>) -> Unit) {")
for (subscription in subscriptions) { for (subscription in subscriptions) {