WIP: Port to compilation on 1.21.4
This commit is contained in:
@@ -7,6 +7,6 @@ public class EarlyRiser implements Runnable {
|
||||
new ClientPlayerRiser().addTinkerers();
|
||||
new HandledScreenRiser().addTinkerers();
|
||||
new SectionBuilderRiser().addTinkerers();
|
||||
new ItemColorsSodiumRiser().addTinkerers();
|
||||
// TODO: new ItemColorsSodiumRiser().addTinkerers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package moe.nea.firmament.init;
|
||||
|
||||
import me.shedaniel.mm.api.ClassTinkerers;
|
||||
import net.minecraft.client.gui.Element;
|
||||
import net.minecraft.client.gui.ParentElement;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package moe.nea.firmament.init;
|
||||
|
||||
import me.shedaniel.mm.api.ClassTinkerers;
|
||||
import moe.nea.firmament.util.ErrorUtil;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.color.item.ItemColorProvider;
|
||||
import net.minecraft.client.color.item.ItemColors;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.InsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.VarInsnNode;
|
||||
|
||||
public class ItemColorsSodiumRiser extends RiserUtils {
|
||||
@IntermediaryName(ItemColors.class)
|
||||
String ItemColors;
|
||||
@IntermediaryName(ItemColorProvider.class)
|
||||
String ItemColorProvider;
|
||||
@IntermediaryName(ItemStack.class)
|
||||
String ItemStack;
|
||||
String getColorProvider = "sodium$getColorProvider";
|
||||
Type getColorProviderDesc = Type.getMethodType(getTypeForClassName(ItemColorProvider),
|
||||
getTypeForClassName(ItemStack));
|
||||
|
||||
@Override
|
||||
public void addTinkerers() {
|
||||
ClassTinkerers.addTransformation(ItemColors, this::addSodiumOverride, true);
|
||||
}
|
||||
|
||||
private void addSodiumOverride(ClassNode classNode) {
|
||||
var node = findMethod(classNode, getColorProvider, getColorProviderDesc);
|
||||
if (node == null) {
|
||||
if (!FabricLoader.getInstance().isModLoaded("sodium"))
|
||||
ErrorUtil.INSTANCE.softError("Sodium is present, but sodium color override could not be injected.");
|
||||
return;
|
||||
}
|
||||
var p = node.instructions.getFirst();
|
||||
while (p != null) {
|
||||
if (p.getOpcode() == Opcodes.ARETURN) {
|
||||
node.instructions.insertBefore(
|
||||
p,
|
||||
mkOverrideSodiumCall()
|
||||
);
|
||||
}
|
||||
p = p.getNext();
|
||||
}
|
||||
}
|
||||
|
||||
private InsnList mkOverrideSodiumCall() {
|
||||
var insnList = new InsnList();
|
||||
insnList.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
insnList.add(new InsnNode(Opcodes.SWAP));
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
|
||||
getTypeForClassName(ItemColors).getInternalName(),
|
||||
"overrideSodium_firmament",
|
||||
Type.getMethodType(getTypeForClassName(ItemColorProvider),
|
||||
getTypeForClassName(ItemColorProvider)).getDescriptor(),
|
||||
false));
|
||||
return insnList;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.mixins;
|
||||
|
||||
import moe.nea.firmament.events.CustomItemModelEvent;
|
||||
import moe.nea.firmament.features.texturepack.CustomGlobalTextures;
|
||||
import net.minecraft.client.render.item.ItemModels;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.BakedModelManager;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ItemModels.class)
|
||||
public class CustomModelEventPatch {
|
||||
|
||||
@Inject(method = "getModel(Lnet/minecraft/item/ItemStack;)Lnet/minecraft/client/render/model/BakedModel;", at = @At("HEAD"), cancellable = true)
|
||||
public void onGetModel(ItemStack stack, CallbackInfoReturnable<BakedModel> cir) {
|
||||
var $this = (ItemModels) (Object) this;
|
||||
var model = CustomItemModelEvent.getModel(stack, $this);
|
||||
if (model == null) {
|
||||
model = CustomGlobalTextures.replaceGlobalModel($this, stack);
|
||||
}
|
||||
if (model != null) {
|
||||
cir.setReturnValue(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.mixins;
|
||||
|
||||
import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures;
|
||||
import net.minecraft.block.SkullBlock;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.block.entity.SkullBlockEntityRenderer;
|
||||
import net.minecraft.component.type.ProfileComponent;
|
||||
import 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(SkullBlockEntityRenderer.class)
|
||||
public class CustomSkullTexturePatch {
|
||||
@Inject(method = "getRenderLayer", at = @At("HEAD"), cancellable = true)
|
||||
private static void onGetRenderLayer(SkullBlock.SkullType type, ProfileComponent profile, CallbackInfoReturnable<RenderLayer> cir) {
|
||||
CustomSkyBlockTextures.INSTANCE.modifySkullTexture(type, profile, cir);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
@Mixin(ClientPlayNetworkHandler.class)
|
||||
public abstract class IncomingPacketListenerPatches {
|
||||
|
||||
|
||||
@ModifyExpressionValue(method = "onCommandTree", at = @At(value = "NEW", target = "(Lcom/mojang/brigadier/tree/RootCommandNode;)Lcom/mojang/brigadier/CommandDispatcher;", remap = false))
|
||||
public CommandDispatcher onOnCommandTree(CommandDispatcher dispatcher) {
|
||||
MaskCommands.Companion.publish(new MaskCommands(dispatcher));
|
||||
@@ -31,7 +30,7 @@ public abstract class IncomingPacketListenerPatches {
|
||||
packet.getParameters(),
|
||||
new Vec3d(packet.getX(), packet.getY(), packet.getZ()),
|
||||
new Vector3f(packet.getOffsetX(), packet.getOffsetY(), packet.getOffsetZ()),
|
||||
packet.isLongDistance(),
|
||||
packet.isImportant(),
|
||||
packet.getCount(),
|
||||
packet.getSpeed()
|
||||
);
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package moe.nea.firmament.mixins;
|
||||
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import moe.nea.firmament.features.texturepack.CustomTextColors;
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.screen.ingame.AnvilScreen;
|
||||
import net.minecraft.client.gui.screen.ingame.BeaconScreen;
|
||||
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||
import net.minecraft.client.gui.screen.ingame.InventoryScreen;
|
||||
import net.minecraft.client.gui.screen.ingame.MerchantScreen;
|
||||
import net.minecraft.text.Text;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin({HandledScreen.class, InventoryScreen.class, CreativeInventoryScreen.class, MerchantScreen.class,
|
||||
AnvilScreen.class, BeaconScreen.class})
|
||||
public class ReplaceTextColorInHandledScreen {
|
||||
|
||||
// To my future self: double check those mixins, but don't be too concerned about errors. Some of the wrapopertions
|
||||
// only apply in some of the specified subclasses.
|
||||
|
||||
@WrapOperation(
|
||||
method = "drawForeground",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/gui/DrawContext;drawText(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;IIIZ)I"),
|
||||
expect = 0,
|
||||
require = 0)
|
||||
private int replaceTextColorWithVariableShadow(DrawContext instance, TextRenderer textRenderer, Text text, int x, int y, int color, boolean shadow, Operation<Integer> original) {
|
||||
return original.call(instance, textRenderer, text, x, y, CustomTextColors.INSTANCE.mapTextColor(text, color), shadow);
|
||||
}
|
||||
|
||||
@WrapOperation(
|
||||
method = "drawForeground",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/gui/DrawContext;drawTextWithShadow(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;III)I"),
|
||||
expect = 0,
|
||||
require = 0)
|
||||
private int replaceTextColorWithShadow(DrawContext instance, TextRenderer textRenderer, Text text, int x, int y, int color, Operation<Integer> original) {
|
||||
return original.call(instance, textRenderer, text, x, y, CustomTextColors.INSTANCE.mapTextColor(text, color));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
|
||||
import moe.nea.firmament.features.texturepack.BakedModelExtra;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.item.ItemRenderer;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ModelTransformationMode;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ItemRenderer.class)
|
||||
public class ApplyHeadModelInItemRenderer {
|
||||
@Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;ZF)V",
|
||||
at = @At("HEAD"))
|
||||
private void applyHeadModel(ItemStack stack, ModelTransformationMode transformationMode, boolean leftHanded,
|
||||
MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay,
|
||||
BakedModel model, boolean useInventoryModel, float z, CallbackInfo ci,
|
||||
@Local(argsOnly = true) LocalRef<BakedModel> modelMut
|
||||
) {
|
||||
var extra = BakedModelExtra.cast(model);
|
||||
if (transformationMode == ModelTransformationMode.HEAD && extra != null) {
|
||||
var headModel = extra.getHeadModel_firmament();
|
||||
if (headModel != null) {
|
||||
modelMut.set(headModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import moe.nea.firmament.features.texturepack.BakedModelExtra;
|
||||
import moe.nea.firmament.features.texturepack.TintOverrides;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.BasicBakedModel;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(BasicBakedModel.class)
|
||||
public class BakedModelDataHolderBasic implements BakedModelExtra {
|
||||
|
||||
@Unique
|
||||
private BakedModel headModel;
|
||||
|
||||
@Unique
|
||||
@Nullable
|
||||
private TintOverrides tintOverrides;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BakedModel getHeadModel_firmament() {
|
||||
return headModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeadModel_firmament(@Nullable BakedModel headModel) {
|
||||
this.headModel = headModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TintOverrides getTintOverrides_firmament() {
|
||||
return tintOverrides;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTintOverrides_firmament(@Nullable TintOverrides tintOverrides) {
|
||||
this.tintOverrides = tintOverrides;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import moe.nea.firmament.features.texturepack.BakedModelExtra;
|
||||
import moe.nea.firmament.features.texturepack.TintOverrides;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.BuiltinBakedModel;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(BuiltinBakedModel.class)
|
||||
public class BakedModelDataHolderBuiltin implements BakedModelExtra {
|
||||
|
||||
@Unique
|
||||
@Nullable
|
||||
private BakedModel headModel;
|
||||
|
||||
@Unique
|
||||
@Nullable
|
||||
private TintOverrides tintOverrides;
|
||||
|
||||
@Override
|
||||
public @Nullable TintOverrides getTintOverrides_firmament() {
|
||||
return tintOverrides;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTintOverrides_firmament(@Nullable TintOverrides tintOverrides) {
|
||||
this.tintOverrides = tintOverrides;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BakedModel getHeadModel_firmament() {
|
||||
return headModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeadModel_firmament(@Nullable BakedModel headModel) {
|
||||
this.headModel = headModel;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import moe.nea.firmament.features.texturepack.BakedOverrideData;
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate;
|
||||
import net.minecraft.client.render.model.json.ModelOverrideList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(ModelOverrideList.BakedOverride.class)
|
||||
public class BakedOverrideDataHolder implements BakedOverrideData {
|
||||
|
||||
@Unique
|
||||
private FirmamentModelPredicate[] firmamentOverrides;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FirmamentModelPredicate[] getFirmamentOverrides() {
|
||||
return firmamentOverrides;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFirmamentOverrides(@NotNull FirmamentModelPredicate[] overrides) {
|
||||
this.firmamentOverrides = overrides;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.features.texturepack.BakedModelExtra;
|
||||
import net.minecraft.block.AbstractSkullBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.entity.LivingEntityRenderer;
|
||||
import net.minecraft.client.render.entity.feature.HeadFeatureRenderer;
|
||||
import net.minecraft.client.render.entity.model.EntityModel;
|
||||
import net.minecraft.client.render.entity.model.ModelWithHead;
|
||||
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.EquipmentSlot;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(HeadFeatureRenderer.class)
|
||||
public class HeadModelReplacerPatch<S extends LivingEntityRenderState, M extends EntityModel<S> & ModelWithHead> {
|
||||
/**
|
||||
* This class serves to disable the replacing of head models with the vanilla block model. Vanilla first selects loads
|
||||
* the model containing the head model regularly in {@link LivingEntityRenderer#updateRenderState}, but then discards
|
||||
* the model in {@link HeadFeatureRenderer#render(MatrixStack, VertexConsumerProvider, int, LivingEntityRenderState, float, float)}
|
||||
* if it detects a skull block. This serves to disable that functionality if a head model override is present.
|
||||
*/
|
||||
@WrapOperation(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/LivingEntityRenderState;FF)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/item/BlockItem;getBlock()Lnet/minecraft/block/Block;"))
|
||||
private Block replaceSkull(BlockItem instance, Operation<Block> original, @Local BakedModel bakedModel) {
|
||||
var oldBlock = original.call(instance);
|
||||
if (oldBlock instanceof AbstractSkullBlock) {
|
||||
var extra = BakedModelExtra.cast(bakedModel);
|
||||
if (extra != null && extra.getHeadModel_firmament() != null)
|
||||
return Blocks.ENCHANTING_TABLE; // Any non skull block. Let's choose the enchanting table because it is very distinct.
|
||||
}
|
||||
return oldBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* We disable the has model override, since texture packs get precedent to server data.
|
||||
*/
|
||||
@WrapOperation(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/LivingEntityRenderState;FF)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/feature/ArmorFeatureRenderer;hasModel(Lnet/minecraft/item/ItemStack;Lnet/minecraft/entity/EquipmentSlot;)Z"))
|
||||
private boolean replaceHasModel(ItemStack stack, EquipmentSlot slot, Operation<Boolean> original,
|
||||
@Local BakedModel bakedModel) {
|
||||
var extra = BakedModelExtra.cast(bakedModel);
|
||||
if (extra != null && extra.getHeadModel_firmament() != null)
|
||||
return false;
|
||||
return original.call(stack, slot);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import moe.nea.firmament.features.texturepack.TintOverrides;
|
||||
import moe.nea.firmament.init.ItemColorsSodiumRiser;
|
||||
import net.minecraft.client.color.item.ItemColorProvider;
|
||||
import net.minecraft.client.color.item.ItemColors;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(ItemColors.class)
|
||||
public class ItemColorRemovalPatch {
|
||||
|
||||
/**
|
||||
* @see ItemColorsSodiumRiser
|
||||
*/
|
||||
private @Nullable ItemColorProvider overrideSodium_firmament(@Nullable ItemColorProvider original) {
|
||||
var tintOverrides = TintOverrides.Companion.getCurrentOverrides();
|
||||
if (!tintOverrides.hasOverrides()) return original;
|
||||
return (stack, tintIndex) -> {
|
||||
var override = tintOverrides.getOverride(tintIndex);
|
||||
if (override != null) return override;
|
||||
if (original != null) return original.getColor(stack, tintIndex);
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Inject(method = "getColor", at = @At("HEAD"), cancellable = true)
|
||||
private void overrideGetColorCall(ItemStack item, int tintIndex, CallbackInfoReturnable<Integer> cir) {
|
||||
var tintOverrides = TintOverrides.Companion.getCurrentOverrides();
|
||||
var override = tintOverrides.getOverride(tintIndex);
|
||||
if (override != null)
|
||||
cir.setReturnValue(override);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.features.texturepack.JsonUnbakedModelFirmExtra;
|
||||
import net.minecraft.client.render.model.json.ItemModelGenerator;
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(ItemModelGenerator.class)
|
||||
public class ItemModelGeneratorJsonUnbakedModelCopy {
|
||||
@ModifyReturnValue(method = "create", at = @At("RETURN"))
|
||||
private JsonUnbakedModel copyExtraModelData(JsonUnbakedModel original, @Local(argsOnly = true) JsonUnbakedModel oldModel) {
|
||||
var extra = ((JsonUnbakedModelFirmExtra) original);
|
||||
var oldExtra = ((JsonUnbakedModelFirmExtra) oldModel);
|
||||
extra.setHeadModel_firmament(oldExtra.getHeadModel_firmament());
|
||||
extra.setTintOverrides_firmament(oldExtra.getTintOverrides_firmament());
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import moe.nea.firmament.features.texturepack.BakedModelExtra;
|
||||
import moe.nea.firmament.features.texturepack.TintOverrides;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.item.ItemRenderer;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ModelTransformationMode;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(value = ItemRenderer.class, priority = 1010)
|
||||
public class ItemRendererTintContextPatch {
|
||||
@Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;ZF)V",
|
||||
at = @At(value = "HEAD"), allow = 1)
|
||||
private void onStartRendering(ItemStack stack, ModelTransformationMode transformationMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, boolean useInventoryModel, float z, CallbackInfo ci) {
|
||||
var extra = BakedModelExtra.cast(model);
|
||||
if (extra != null) {
|
||||
TintOverrides.Companion.enter(extra.getTintOverrides_firmament());
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;ZF)V",
|
||||
at = @At("TAIL"), allow = 1)
|
||||
private void onEndRendering(ItemStack stack, ModelTransformationMode transformationMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, boolean useInventoryModel, float z, CallbackInfo ci) {
|
||||
var extra = BakedModelExtra.cast(model);
|
||||
if (extra != null) {
|
||||
TintOverrides.Companion.exit(extra.getTintOverrides_firmament());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.features.texturepack.BakedModelExtra;
|
||||
import moe.nea.firmament.features.texturepack.JsonUnbakedModelFirmExtra;
|
||||
import moe.nea.firmament.features.texturepack.TintOverrides;
|
||||
import moe.nea.firmament.util.ErrorUtil;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.Baker;
|
||||
import net.minecraft.client.render.model.ModelRotation;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Mixin(JsonUnbakedModel.class)
|
||||
public abstract class JsonUnbakedModelDataHolder implements JsonUnbakedModelFirmExtra {
|
||||
@Shadow
|
||||
@Nullable
|
||||
protected JsonUnbakedModel parent;
|
||||
|
||||
@Shadow
|
||||
public abstract String toString();
|
||||
|
||||
@Unique
|
||||
@Nullable
|
||||
public Identifier headModel;
|
||||
@Unique
|
||||
@Nullable
|
||||
public TintOverrides tintOverrides;
|
||||
@Unique
|
||||
@Nullable
|
||||
public TintOverrides mergedTintOverrides;
|
||||
|
||||
@Override
|
||||
public void setTintOverrides_firmament(@Nullable TintOverrides tintOverrides) {
|
||||
this.tintOverrides = tintOverrides;
|
||||
this.mergedTintOverrides = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TintOverrides getTintOverrides_firmament() {
|
||||
if (mergedTintOverrides != null)
|
||||
return mergedTintOverrides;
|
||||
var mergedTintOverrides = parent == null ? new TintOverrides()
|
||||
: ((JsonUnbakedModelFirmExtra) parent).getTintOverrides_firmament();
|
||||
if (tintOverrides != null)
|
||||
mergedTintOverrides = tintOverrides.mergeWithParent(mergedTintOverrides);
|
||||
this.mergedTintOverrides = mergedTintOverrides;
|
||||
return mergedTintOverrides;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeadModel_firmament(@Nullable Identifier identifier) {
|
||||
this.headModel = identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Identifier getHeadModel_firmament() {
|
||||
if (this.headModel != null) return this.headModel;
|
||||
if (this.parent == null) return null;
|
||||
return ((JsonUnbakedModelFirmExtra) this.parent).getHeadModel_firmament();
|
||||
}
|
||||
|
||||
@Inject(method = "resolve", at = @At("HEAD"))
|
||||
private void addDependencies(UnbakedModel.Resolver resolver, CallbackInfo ci) {
|
||||
var headModel = getHeadModel_firmament();
|
||||
if (headModel != null) {
|
||||
resolver.resolve(headModel);
|
||||
}
|
||||
}
|
||||
|
||||
private void addExtraBakeInfo(BakedModel bakedModel, Baker baker) {
|
||||
if (!this.toString().contains("minecraft") && this.toString().contains("crimson")) {
|
||||
System.out.println("Found non minecraft model " + this);
|
||||
}
|
||||
var extra = BakedModelExtra.cast(bakedModel);
|
||||
if (extra != null) {
|
||||
var headModel = getHeadModel_firmament();
|
||||
if (headModel != null) {
|
||||
extra.setHeadModel_firmament(baker.bake(headModel, ModelRotation.X0_Y0));
|
||||
}
|
||||
if (getTintOverrides_firmament().hasOverrides()) {
|
||||
extra.setTintOverrides_firmament(getTintOverrides_firmament());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ProvideBakerToJsonUnbakedModelPatch
|
||||
*/
|
||||
@Override
|
||||
public void storeExtraBaker_firmament(@NotNull Baker baker) {
|
||||
this.storedBaker = baker;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private Baker storedBaker;
|
||||
|
||||
@ModifyReturnValue(
|
||||
method = "bake(Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;Z)Lnet/minecraft/client/render/model/BakedModel;",
|
||||
at = @At("RETURN"))
|
||||
private BakedModel bakeExtraInfoWithoutBaker(BakedModel original) {
|
||||
if (storedBaker != null) {
|
||||
addExtraBakeInfo(original, storedBaker);
|
||||
storedBaker = null;
|
||||
}
|
||||
return original;
|
||||
}
|
||||
|
||||
@ModifyReturnValue(
|
||||
method = {
|
||||
"bake(Lnet/minecraft/client/render/model/Baker;Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;)Lnet/minecraft/client/render/model/BakedModel;"
|
||||
},
|
||||
at = @At(value = "RETURN"))
|
||||
private BakedModel bakeExtraInfo(BakedModel original, @Local(argsOnly = true) Baker baker) {
|
||||
addExtraBakeInfo(original, baker);
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate;
|
||||
import moe.nea.firmament.features.texturepack.ModelOverrideData;
|
||||
import net.minecraft.client.render.model.json.ModelOverride;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(ModelOverride.class)
|
||||
public class ModelOverrideDataHolder implements ModelOverrideData {
|
||||
|
||||
@Unique
|
||||
private FirmamentModelPredicate[] overrides;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FirmamentModelPredicate[] getFirmamentOverrides() {
|
||||
return overrides;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFirmamentOverrides(@NotNull FirmamentModelPredicate[] overrides) {
|
||||
this.overrides = overrides;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +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.CustomGlobalArmorOverrides;
|
||||
import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer;
|
||||
import net.minecraft.component.type.EquippableComponent;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(ArmorFeatureRenderer.class)
|
||||
public class PatchArmorTexture {
|
||||
@WrapOperation(
|
||||
method = "renderArmor",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/component/type/EquippableComponent;model()Ljava/util/Optional;"))
|
||||
private Optional<Identifier> overrideLayers(
|
||||
EquippableComponent instance, Operation<Optional<Identifier>> original, @Local(argsOnly = true) ItemStack itemStack
|
||||
) {
|
||||
// TODO: check that all armour items are naturally equippable and have the equppable component. otherwise our call here will not be reached.
|
||||
var overrides = CustomGlobalArmorOverrides.overrideArmor(itemStack);
|
||||
return overrides.or(() -> original.call(instance));
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.features.texturepack.JsonUnbakedModelFirmExtra;
|
||||
import moe.nea.firmament.features.texturepack.TintOverrides;
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(JsonUnbakedModel.Deserializer.class)
|
||||
public class PatchJsonUnbakedModelDeserializer {
|
||||
@ModifyReturnValue(method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/render/model/json/JsonUnbakedModel;",
|
||||
at = @At("RETURN"))
|
||||
private JsonUnbakedModel addHeadModel(JsonUnbakedModel original, @Local JsonObject jsonObject) {
|
||||
var headModel = jsonObject.get("firmament:head_model");
|
||||
var extra = ((JsonUnbakedModelFirmExtra) original);
|
||||
if (headModel instanceof JsonPrimitive prim && prim.isString()) {
|
||||
extra.setHeadModel_firmament(Identifier.of(prim.getAsString()));
|
||||
}
|
||||
var tintOverrides = jsonObject.get("firmament:tint_overrides");
|
||||
if (tintOverrides instanceof JsonObject object) {
|
||||
extra.setTintOverrides_firmament(TintOverrides.Companion.parse(object));
|
||||
}
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import moe.nea.firmament.features.texturepack.CustomGlobalArmorOverrides;
|
||||
import net.minecraft.client.render.entity.equipment.EquipmentModelLoader;
|
||||
import net.minecraft.client.render.entity.equipment.EquipmentRenderer;
|
||||
import net.minecraft.item.equipment.EquipmentModel;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(EquipmentRenderer.class)
|
||||
public class PatchLegacyArmorLayerSupport {
|
||||
@WrapOperation(method = "render(Lnet/minecraft/item/equipment/EquipmentModel$LayerType;Lnet/minecraft/util/Identifier;Lnet/minecraft/client/model/Model;Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/util/Identifier;)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/equipment/EquipmentModelLoader;get(Lnet/minecraft/util/Identifier;)Lnet/minecraft/item/equipment/EquipmentModel;"))
|
||||
private EquipmentModel patchModelLayers(EquipmentModelLoader instance, Identifier id, Operation<EquipmentModel> original) {
|
||||
var modelOverride = CustomGlobalArmorOverrides.overrideArmorLayer(id);
|
||||
if (modelOverride != null) return modelOverride;
|
||||
return original.call(instance, id);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.features.texturepack.CustomModelOverrideParser;
|
||||
import moe.nea.firmament.features.texturepack.ModelOverrideData;
|
||||
import net.minecraft.client.render.model.json.ModelOverride;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ModelOverride.Deserializer.class)
|
||||
public class PatchOverrideDeserializer {
|
||||
|
||||
@ModifyReturnValue(
|
||||
method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/render/model/json/ModelOverride;",
|
||||
at = @At(value = "RETURN"))
|
||||
private ModelOverride addCustomOverrides(ModelOverride original, @Local JsonObject jsonObject) {
|
||||
var originalData = (ModelOverrideData) (Object) original;
|
||||
originalData.setFirmamentOverrides(CustomModelOverrideParser.parseCustomModelOverrides(jsonObject));
|
||||
return original;
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(
|
||||
method = "deserializeMinPropertyValues(Lcom/google/gson/JsonObject;)Ljava/util/List;",
|
||||
at = @At(value = "INVOKE", target = "Ljava/util/Map$Entry;getValue()Ljava/lang/Object;"))
|
||||
private Object removeFirmamentPredicatesFromJsonIteration(Object original, @Local Map.Entry<String, JsonElement> entry) {
|
||||
if (entry.getKey().startsWith("firmament:")) return new JsonPrimitive(0F);
|
||||
return original;
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "deserializeMinPropertyValues",
|
||||
at = @At(value = "INVOKE", target = "Ljava/util/Map;entrySet()Ljava/util/Set;")
|
||||
)
|
||||
private void whatever(JsonObject object, CallbackInfoReturnable<List<ModelOverride.Condition>> cir,
|
||||
@Local Map<Identifier, Float> maps) {
|
||||
maps.entrySet().removeIf(it -> it.getKey().getNamespace().equals("firmament"));
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import moe.nea.firmament.features.texturepack.JsonUnbakedModelFirmExtra;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.Baker;
|
||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @see JsonUnbakedModelDataHolder#storeExtraBaker_firmament
|
||||
*/
|
||||
@Mixin(targets = "net.minecraft.client.render.model.ModelBaker$BakerImpl")
|
||||
public abstract class ProvideBakerToJsonUnbakedModelPatch implements Baker {
|
||||
@WrapOperation(method = "bake(Lnet/minecraft/client/render/model/UnbakedModel;Lnet/minecraft/client/render/model/ModelBakeSettings;)Lnet/minecraft/client/render/model/BakedModel;", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/json/JsonUnbakedModel;bake(Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;Z)Lnet/minecraft/client/render/model/BakedModel;"))
|
||||
private BakedModel provideExtraBakerToModel(JsonUnbakedModel instance, Function<SpriteIdentifier, Sprite> function, ModelBakeSettings modelBakeSettings, boolean bl, Operation<BakedModel> original) {
|
||||
((JsonUnbakedModelFirmExtra) instance).storeExtraBaker_firmament(this);
|
||||
return original.call(instance, function, modelBakeSettings, bl);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import moe.nea.firmament.events.BakeExtraModelsEvent;
|
||||
import net.minecraft.client.render.model.BlockStatesLoader;
|
||||
import net.minecraft.client.render.model.ItemModel;
|
||||
import net.minecraft.client.render.model.ReferencedModelsCollector;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.util.ModelIdentifier;
|
||||
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.CallbackInfo;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ReferencedModelsCollector.class)
|
||||
public abstract class ReferenceCustomModelsPatch {
|
||||
@Shadow
|
||||
protected abstract void addTopLevelModel(ModelIdentifier modelId, UnbakedModel model);
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Map<Identifier, UnbakedModel> inputs;
|
||||
|
||||
@Inject(method = "addBlockStates", at = @At("RETURN"))
|
||||
private void addFirmamentReferencedModels(
|
||||
BlockStatesLoader.BlockStateDefinition definition, CallbackInfo ci
|
||||
) {
|
||||
BakeExtraModelsEvent.Companion.publish(new BakeExtraModelsEvent(
|
||||
(modelIdentifier, identifier) -> addTopLevelModel(modelIdentifier, new ItemModel(identifier))));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.features.texturepack.CustomBlockTextures;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
import net.minecraft.sound.BlockSoundGroup;
|
||||
import net.minecraft.sound.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(WorldRenderer.class)
|
||||
public class ReplaceBlockBreakSoundPatch {
|
||||
// Sadly hypixel does not send a world event here and instead plays the sound on the server directly
|
||||
// @WrapOperation(method = "processWorldEvent", at = @At(value = "INVOKE", target = "Lnet/minecraft/sound/BlockSoundGroup;getBreakSound()Lnet/minecraft/sound/SoundEvent;"))
|
||||
// private SoundEvent replaceBreakSoundEvent(BlockSoundGroup instance, Operation<SoundEvent> original,
|
||||
// @Local(argsOnly = true) BlockPos pos, @Local BlockState blockState) {
|
||||
// var replacement = CustomBlockTextures.getReplacement(blockState, pos);
|
||||
// if (replacement != null && replacement.getSound() != null) {
|
||||
// return SoundEvent.of(replacement.getSound());
|
||||
// }
|
||||
// return original.call(instance);
|
||||
// }
|
||||
}
|
||||
@@ -1,30 +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.CustomBlockTextures;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.network.ClientPlayerInteractionManager;
|
||||
import net.minecraft.client.sound.PositionedSoundInstance;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(ClientPlayerInteractionManager.class)
|
||||
public class ReplaceBlockHitSoundPatch {
|
||||
@WrapOperation(method = "updateBlockBreakingProgress", at = @At(value = "NEW", target = "(Lnet/minecraft/sound/SoundEvent;Lnet/minecraft/sound/SoundCategory;FFLnet/minecraft/util/math/random/Random;Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/client/sound/PositionedSoundInstance;"))
|
||||
private PositionedSoundInstance replaceSound(
|
||||
SoundEvent sound, SoundCategory category, float volume, float pitch,
|
||||
Random random, BlockPos pos, Operation<PositionedSoundInstance> original,
|
||||
@Local BlockState blockState) {
|
||||
var replacement = CustomBlockTextures.getReplacement(blockState, pos);
|
||||
if (replacement != null && replacement.getSound() != null) {
|
||||
sound = SoundEvent.of(replacement.getSound());
|
||||
}
|
||||
return original.call(sound, category, volume, pitch, random, pos);
|
||||
}
|
||||
}
|
||||
@@ -1,38 +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.CustomBlockTextures;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.block.BlockModels;
|
||||
import net.minecraft.client.render.block.BlockRenderManager;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(BlockRenderManager.class)
|
||||
public class ReplaceBlockRenderManagerBlockModel {
|
||||
@WrapOperation(method = "renderBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/block/BlockRenderManager;getModel(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/model/BakedModel;"))
|
||||
private BakedModel replaceModelInRenderBlock(
|
||||
BlockRenderManager instance, BlockState state, Operation<BakedModel> original, @Local(argsOnly = true) BlockPos pos) {
|
||||
var replacement = CustomBlockTextures.getReplacementModel(state, pos);
|
||||
if (replacement != null) return replacement;
|
||||
CustomBlockTextures.enterFallbackCall();
|
||||
var fallback = original.call(instance, state);
|
||||
CustomBlockTextures.exitFallbackCall();
|
||||
return fallback;
|
||||
}
|
||||
|
||||
@WrapOperation(method = "renderDamage", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/block/BlockModels;getModel(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/model/BakedModel;"))
|
||||
private BakedModel replaceModelInRenderDamage(
|
||||
BlockModels instance, BlockState state, Operation<BakedModel> original, @Local(argsOnly = true) BlockPos pos) {
|
||||
var replacement = CustomBlockTextures.getReplacementModel(state, pos);
|
||||
if (replacement != null) return replacement;
|
||||
CustomBlockTextures.enterFallbackCall();
|
||||
var fallback = original.call(instance, state);
|
||||
CustomBlockTextures.exitFallbackCall();
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import moe.nea.firmament.features.texturepack.CustomBlockTextures;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.block.BlockModels;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
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(BlockModels.class)
|
||||
public class ReplaceFallbackBlockModel {
|
||||
// TODO: add check to BlockDustParticle
|
||||
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)
|
||||
private void getModel(BlockState state, CallbackInfoReturnable<BakedModel> cir) {
|
||||
var replacement = CustomBlockTextures.getReplacementModel(state, null);
|
||||
if (replacement != null)
|
||||
cir.setReturnValue(replacement);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.mixins.custommodels;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import moe.nea.firmament.Firmament;
|
||||
import moe.nea.firmament.features.texturepack.BakedOverrideData;
|
||||
import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures;
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate;
|
||||
import moe.nea.firmament.features.texturepack.ModelOverrideData;
|
||||
import net.minecraft.client.render.model.json.ModelOverride;
|
||||
import net.minecraft.client.render.model.json.ModelOverrideList;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Mixin(ModelOverrideList.class)
|
||||
public class TestForFirmamentOverridePredicatesPatch {
|
||||
|
||||
@Shadow
|
||||
private Identifier[] conditionTypes;
|
||||
|
||||
@ModifyArg(method = "<init>(Lnet/minecraft/client/render/model/Baker;Ljava/util/List;)V",
|
||||
at = @At(
|
||||
value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z"
|
||||
))
|
||||
public Object onInit(
|
||||
Object element,
|
||||
@Local ModelOverride modelOverride
|
||||
) {
|
||||
var bakedOverride = (ModelOverrideList.BakedOverride) element;
|
||||
var modelOverrideData = ModelOverrideData.cast(modelOverride);
|
||||
BakedOverrideData.cast(bakedOverride)
|
||||
.setFirmamentOverrides(modelOverrideData.getFirmamentOverrides());
|
||||
if (conditionTypes.length == 0 &&
|
||||
modelOverrideData.getFirmamentOverrides() != null &&
|
||||
modelOverrideData.getFirmamentOverrides().length > 0) {
|
||||
conditionTypes = new Identifier[]{Firmament.INSTANCE.identifier("sentinel/enforce_model_override_evaluation")};
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(method = "getModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride;test([F)Z"))
|
||||
public boolean testFirmamentOverrides(boolean originalValue,
|
||||
@Local ModelOverrideList.BakedOverride bakedOverride,
|
||||
@Local(argsOnly = true) ItemStack stack) {
|
||||
if (!originalValue) return false;
|
||||
var overrideData = (BakedOverrideData) (Object) bakedOverride;
|
||||
var overrides = overrideData.getFirmamentOverrides();
|
||||
if (overrides == null) return true;
|
||||
if (!CustomSkyBlockTextures.TConfig.INSTANCE.getEnableModelOverrides()) return false;
|
||||
for (FirmamentModelPredicate firmamentOverride : overrides) {
|
||||
if (!firmamentOverride.test(stack))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package moe.nea.firmament.events
|
||||
|
||||
import java.util.function.BiConsumer
|
||||
import net.minecraft.client.item.ItemAssetsLoader
|
||||
import net.minecraft.client.render.model.ReferencedModelsCollector
|
||||
import net.minecraft.client.util.ModelIdentifier
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
// TODO: Rename this event, since it is not really directly baking models anymore
|
||||
// TODO: This event may be removed now since ItemAssetsLoader seems to load all item models now (probably to cope with servers setting the item_model component). Check whether this also applies to blocks now.
|
||||
//@Deprecated(level = DeprecationLevel.ERROR, message = "This is no longer needed, since ItemAssetsLoader loads all item models.")
|
||||
class BakeExtraModelsEvent(
|
||||
private val addAnyModel: BiConsumer<ModelIdentifier, Identifier>,
|
||||
) : FirmamentEvent() {
|
||||
@@ -15,10 +17,13 @@ class BakeExtraModelsEvent(
|
||||
}
|
||||
|
||||
fun addItemModel(modelIdentifier: ModelIdentifier) {
|
||||
addNonItemModel(
|
||||
modelIdentifier,
|
||||
modelIdentifier.id.withPrefixedPath(ReferencedModelsCollector.ITEM_DIRECTORY))
|
||||
// TODO: If this is still needed: ItemAssetsLoader.FINDER
|
||||
// addNonItemModel(
|
||||
// modelIdentifier,
|
||||
// modelIdentifier.id.withPrefixedPath())
|
||||
}
|
||||
|
||||
// @Deprecated(level = DeprecationLevel.ERROR, message = "This is no longer needed, since ItemAssetsLoader loads all item models.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : FirmamentEventBus<BakeExtraModelsEvent>()
|
||||
}
|
||||
|
||||
@@ -1,38 +1,23 @@
|
||||
package moe.nea.firmament.events
|
||||
|
||||
import java.util.Optional
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.client.render.item.ItemModels
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.util.ModelIdentifier
|
||||
import net.minecraft.item.ItemStack
|
||||
import moe.nea.firmament.util.ErrorUtil
|
||||
import moe.nea.firmament.util.collections.WeakCache
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
// TODO: assert an order on these events
|
||||
data class CustomItemModelEvent(
|
||||
val itemStack: ItemStack,
|
||||
var overrideModel: ModelIdentifier? = null,
|
||||
var overrideModel: Identifier? = null,
|
||||
) : FirmamentEvent() {
|
||||
companion object : FirmamentEventBus<CustomItemModelEvent>() {
|
||||
val cache =
|
||||
WeakCache.memoize<ItemStack, ItemModels, Optional<BakedModel>>("CustomItemModels") { stack, models ->
|
||||
val modelId = getModelIdentifier(stack) ?: return@memoize Optional.empty()
|
||||
ErrorUtil.softCheck("Model Id needs to have an inventory variant", modelId.variant() == "inventory")
|
||||
val bakedModel = models.getModel(modelId.id)
|
||||
if (bakedModel == null || bakedModel === models.missingModelSupplier.get()) return@memoize Optional.empty()
|
||||
Optional.of(bakedModel)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getModelIdentifier(itemStack: ItemStack?): ModelIdentifier? {
|
||||
fun getModelIdentifier(itemStack: ItemStack?): Identifier? {
|
||||
// TODO: Re-add memoization and add an error / warning if the model does not exist
|
||||
if (itemStack == null) return null
|
||||
return publish(CustomItemModelEvent(itemStack)).overrideModel
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getModel(itemStack: ItemStack?, thing: ItemModels): BakedModel? {
|
||||
if (itemStack == null) return null
|
||||
return cache.invoke(itemStack, thing).getOrNull()
|
||||
}
|
||||
fun overrideIfExists(overrideModel: Identifier) {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import moe.nea.firmament.features.inventory.buttons.InventoryButtons
|
||||
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlay
|
||||
import moe.nea.firmament.features.mining.PickaxeAbility
|
||||
import moe.nea.firmament.features.mining.PristineProfitTracker
|
||||
import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures
|
||||
import moe.nea.firmament.features.world.FairySouls
|
||||
import moe.nea.firmament.features.world.Waypoints
|
||||
import moe.nea.firmament.util.data.DataHolder
|
||||
@@ -70,7 +69,6 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
|
||||
loadFeature(QuickCommands)
|
||||
loadFeature(PetFeatures)
|
||||
loadFeature(SaveCursorPosition)
|
||||
loadFeature(CustomSkyBlockTextures)
|
||||
loadFeature(PriceData)
|
||||
loadFeature(Fixes)
|
||||
loadFeature(DianaWaypoints)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.features.chat
|
||||
|
||||
import io.ktor.client.request.get
|
||||
@@ -7,16 +5,15 @@ import io.ktor.client.statement.bodyAsChannel
|
||||
import io.ktor.utils.io.jvm.javaio.toInputStream
|
||||
import java.net.URL
|
||||
import java.util.Collections
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import moe.nea.jarvis.api.Point
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.async
|
||||
import kotlin.math.min
|
||||
import net.minecraft.client.gui.screen.ChatScreen
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.texture.NativeImage
|
||||
import net.minecraft.client.texture.NativeImageBackedTexture
|
||||
import net.minecraft.scoreboard.ScoreboardCriterion.RenderType
|
||||
import net.minecraft.text.ClickEvent
|
||||
import net.minecraft.text.HoverEvent
|
||||
import net.minecraft.text.Style
|
||||
@@ -35,130 +32,132 @@ import moe.nea.firmament.util.transformEachRecursively
|
||||
import moe.nea.firmament.util.unformattedString
|
||||
|
||||
object ChatLinks : FirmamentFeature {
|
||||
override val identifier: String
|
||||
get() = "chat-links"
|
||||
override val identifier: String
|
||||
get() = "chat-links"
|
||||
|
||||
object TConfig : ManagedConfig(identifier, Category.CHAT) {
|
||||
val enableLinks by toggle("links-enabled") { true }
|
||||
val imageEnabled by toggle("image-enabled") { true }
|
||||
val allowAllHosts by toggle("allow-all-hosts") { false }
|
||||
val allowedHosts by string("allowed-hosts") { "cdn.discordapp.com,media.discordapp.com,media.discordapp.net,i.imgur.com" }
|
||||
val actualAllowedHosts get() = allowedHosts.split(",").map { it.trim() }
|
||||
val position by position("position", 16 * 20, 9 * 20) { Point(0.0, 0.0) }
|
||||
}
|
||||
object TConfig : ManagedConfig(identifier, Category.CHAT) {
|
||||
val enableLinks by toggle("links-enabled") { true }
|
||||
val imageEnabled by toggle("image-enabled") { true }
|
||||
val allowAllHosts by toggle("allow-all-hosts") { false }
|
||||
val allowedHosts by string("allowed-hosts") { "cdn.discordapp.com,media.discordapp.com,media.discordapp.net,i.imgur.com" }
|
||||
val actualAllowedHosts get() = allowedHosts.split(",").map { it.trim() }
|
||||
val position by position("position", 16 * 20, 9 * 20) { Point(0.0, 0.0) }
|
||||
}
|
||||
|
||||
private fun isHostAllowed(host: String) =
|
||||
TConfig.allowAllHosts || TConfig.actualAllowedHosts.any { it.equals(host, ignoreCase = true) }
|
||||
private fun isHostAllowed(host: String) =
|
||||
TConfig.allowAllHosts || TConfig.actualAllowedHosts.any { it.equals(host, ignoreCase = true) }
|
||||
|
||||
private fun isUrlAllowed(url: String) = isHostAllowed(url.removePrefix("https://").substringBefore("/"))
|
||||
private fun isUrlAllowed(url: String) = isHostAllowed(url.removePrefix("https://").substringBefore("/"))
|
||||
|
||||
override val config get() = TConfig
|
||||
val urlRegex = "https://[^. ]+\\.[^ ]+(\\.?( |$))".toRegex()
|
||||
override val config get() = TConfig
|
||||
val urlRegex = "https://[^. ]+\\.[^ ]+(\\.?( |$))".toRegex()
|
||||
val nextTexId = AtomicInteger(0)
|
||||
|
||||
data class Image(
|
||||
val texture: Identifier,
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
)
|
||||
data class Image(
|
||||
val texture: Identifier,
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
)
|
||||
|
||||
val imageCache: MutableMap<String, Deferred<Image?>> =
|
||||
Collections.synchronizedMap(mutableMapOf<String, Deferred<Image?>>())
|
||||
val imageCache: MutableMap<String, Deferred<Image?>> =
|
||||
Collections.synchronizedMap(mutableMapOf<String, Deferred<Image?>>())
|
||||
|
||||
private fun tryCacheUrl(url: String) {
|
||||
if (!isUrlAllowed(url)) {
|
||||
return
|
||||
}
|
||||
if (url in imageCache) {
|
||||
return
|
||||
}
|
||||
imageCache[url] = Firmament.coroutineScope.async {
|
||||
try {
|
||||
val response = Firmament.httpClient.get(URL(url))
|
||||
if (response.status.value == 200) {
|
||||
val inputStream = response.bodyAsChannel().toInputStream(Firmament.globalJob)
|
||||
val image = NativeImage.read(inputStream)
|
||||
val texture = MC.textureManager.registerDynamicTexture(
|
||||
"dynamic_image_preview",
|
||||
NativeImageBackedTexture(image)
|
||||
)
|
||||
Image(texture, image.width, image.height)
|
||||
} else
|
||||
null
|
||||
} catch (exc: Exception) {
|
||||
exc.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun tryCacheUrl(url: String) {
|
||||
if (!isUrlAllowed(url)) {
|
||||
return
|
||||
}
|
||||
if (url in imageCache) {
|
||||
return
|
||||
}
|
||||
imageCache[url] = Firmament.coroutineScope.async {
|
||||
try {
|
||||
val response = Firmament.httpClient.get(URL(url))
|
||||
if (response.status.value == 200) {
|
||||
val inputStream = response.bodyAsChannel().toInputStream(Firmament.globalJob)
|
||||
val image = NativeImage.read(inputStream)
|
||||
val texId = Firmament.identifier("dynamic_image_preview${nextTexId.getAndIncrement()}")
|
||||
MC.textureManager.registerTexture(
|
||||
texId,
|
||||
NativeImageBackedTexture(image)
|
||||
)
|
||||
Image(texId, image.width, image.height)
|
||||
} else
|
||||
null
|
||||
} catch (exc: Exception) {
|
||||
exc.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val imageExtensions = listOf("jpg", "png", "gif", "jpeg")
|
||||
fun isImageUrl(url: String): Boolean {
|
||||
return (url.substringAfterLast('.').lowercase() in imageExtensions)
|
||||
}
|
||||
val imageExtensions = listOf("jpg", "png", "gif", "jpeg")
|
||||
fun isImageUrl(url: String): Boolean {
|
||||
return (url.substringAfterLast('.').lowercase() in imageExtensions)
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun onRender(it: ScreenRenderPostEvent) {
|
||||
if (!TConfig.imageEnabled) return
|
||||
if (it.screen !is ChatScreen) return
|
||||
val hoveredComponent =
|
||||
MC.inGameHud.chatHud.getTextStyleAt(it.mouseX.toDouble(), it.mouseY.toDouble()) ?: return
|
||||
val hoverEvent = hoveredComponent.hoverEvent ?: return
|
||||
val value = hoverEvent.getValue(HoverEvent.Action.SHOW_TEXT) ?: return
|
||||
val url = urlRegex.matchEntire(value.unformattedString)?.groupValues?.get(0) ?: return
|
||||
if (!isImageUrl(url)) return
|
||||
val imageFuture = imageCache[url] ?: return
|
||||
if (!imageFuture.isCompleted) return
|
||||
val image = imageFuture.getCompleted() ?: return
|
||||
it.drawContext.matrices.push()
|
||||
val pos = TConfig.position
|
||||
pos.applyTransformations(it.drawContext.matrices)
|
||||
val scale = min(1F, min((9 * 20F) / image.height, (16 * 20F) / image.width))
|
||||
it.drawContext.matrices.scale(scale, scale, 1F)
|
||||
it.drawContext.drawTexture(
|
||||
image.texture,
|
||||
0,
|
||||
0,
|
||||
1F,
|
||||
1F,
|
||||
image.width,
|
||||
image.height,
|
||||
image.width,
|
||||
image.height,
|
||||
)
|
||||
it.drawContext.matrices.pop()
|
||||
}
|
||||
@Subscribe
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun onRender(it: ScreenRenderPostEvent) {
|
||||
if (!TConfig.imageEnabled) return
|
||||
if (it.screen !is ChatScreen) return
|
||||
val hoveredComponent =
|
||||
MC.inGameHud.chatHud.getTextStyleAt(it.mouseX.toDouble(), it.mouseY.toDouble()) ?: return
|
||||
val hoverEvent = hoveredComponent.hoverEvent ?: return
|
||||
val value = hoverEvent.getValue(HoverEvent.Action.SHOW_TEXT) ?: return
|
||||
val url = urlRegex.matchEntire(value.unformattedString)?.groupValues?.get(0) ?: return
|
||||
if (!isImageUrl(url)) return
|
||||
val imageFuture = imageCache[url] ?: return
|
||||
if (!imageFuture.isCompleted) return
|
||||
val image = imageFuture.getCompleted() ?: return
|
||||
it.drawContext.matrices.push()
|
||||
val pos = TConfig.position
|
||||
pos.applyTransformations(it.drawContext.matrices)
|
||||
val scale = min(1F, min((9 * 20F) / image.height, (16 * 20F) / image.width))
|
||||
it.drawContext.matrices.scale(scale, scale, 1F)
|
||||
it.drawContext.drawTexture(
|
||||
image.texture,
|
||||
0,
|
||||
0,
|
||||
1F,
|
||||
1F,
|
||||
image.width,
|
||||
image.height,
|
||||
image.width,
|
||||
image.height,
|
||||
)
|
||||
it.drawContext.matrices.pop()
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onModifyChat(it: ModifyChatEvent) {
|
||||
if (!TConfig.enableLinks) return
|
||||
it.replaceWith = it.replaceWith.transformEachRecursively { child ->
|
||||
val text = child.string
|
||||
if ("://" !in text) return@transformEachRecursively child
|
||||
val s = Text.empty().setStyle(child.style)
|
||||
var index = 0
|
||||
while (index < text.length) {
|
||||
val nextMatch = urlRegex.find(text, index)
|
||||
if (nextMatch == null) {
|
||||
s.append(Text.literal(text.substring(index, text.length)))
|
||||
break
|
||||
}
|
||||
val range = nextMatch.groups[0]!!.range
|
||||
val url = nextMatch.groupValues[0]
|
||||
s.append(Text.literal(text.substring(index, range.first)))
|
||||
s.append(
|
||||
Text.literal(url).setStyle(
|
||||
Style.EMPTY.withUnderline(true).withColor(
|
||||
Formatting.AQUA
|
||||
).withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(url)))
|
||||
.withClickEvent(ClickEvent(ClickEvent.Action.OPEN_URL, url))
|
||||
)
|
||||
)
|
||||
if (isImageUrl(url))
|
||||
tryCacheUrl(url)
|
||||
index = range.last + 1
|
||||
}
|
||||
s
|
||||
}
|
||||
}
|
||||
@Subscribe
|
||||
fun onModifyChat(it: ModifyChatEvent) {
|
||||
if (!TConfig.enableLinks) return
|
||||
it.replaceWith = it.replaceWith.transformEachRecursively { child ->
|
||||
val text = child.string
|
||||
if ("://" !in text) return@transformEachRecursively child
|
||||
val s = Text.empty().setStyle(child.style)
|
||||
var index = 0
|
||||
while (index < text.length) {
|
||||
val nextMatch = urlRegex.find(text, index)
|
||||
if (nextMatch == null) {
|
||||
s.append(Text.literal(text.substring(index, text.length)))
|
||||
break
|
||||
}
|
||||
val range = nextMatch.groups[0]!!.range
|
||||
val url = nextMatch.groupValues[0]
|
||||
s.append(Text.literal(text.substring(index, range.first)))
|
||||
s.append(
|
||||
Text.literal(url).setStyle(
|
||||
Style.EMPTY.withUnderline(true).withColor(
|
||||
Formatting.AQUA
|
||||
).withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(url)))
|
||||
.withClickEvent(ClickEvent(ClickEvent.Action.OPEN_URL, url))
|
||||
)
|
||||
)
|
||||
if (isImageUrl(url))
|
||||
tryCacheUrl(url)
|
||||
index = range.last + 1
|
||||
}
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.block.SkullBlock
|
||||
import net.minecraft.block.entity.SkullBlockEntity
|
||||
import net.minecraft.component.DataComponentTypes
|
||||
import net.minecraft.component.type.ProfileComponent
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
@@ -12,6 +13,7 @@ import net.minecraft.item.Items
|
||||
import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.text.TextCodecs
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.hit.EntityHitResult
|
||||
import net.minecraft.util.hit.HitResult
|
||||
@@ -23,7 +25,6 @@ import moe.nea.firmament.events.ScreenChangeEvent
|
||||
import moe.nea.firmament.events.TickEvent
|
||||
import moe.nea.firmament.events.WorldKeyboardEvent
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures
|
||||
import moe.nea.firmament.gui.config.ManagedConfig
|
||||
import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
|
||||
import moe.nea.firmament.util.ClipboardUtils
|
||||
@@ -101,6 +102,8 @@ object PowerUserTools : FirmamentFeature {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: leak this through some other way, maybe.
|
||||
lateinit var getSkullId: (profile: ProfileComponent) -> Identifier?
|
||||
|
||||
@Subscribe
|
||||
fun copyInventoryInfo(it: HandledScreenKeyPressedEvent) {
|
||||
@@ -116,7 +119,7 @@ object PowerUserTools : FirmamentFeature {
|
||||
lastCopiedStack =
|
||||
Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skyblockid", sbId.neuItem))
|
||||
} else if (it.matches(TConfig.copyTexturePackId)) {
|
||||
val model = CustomItemModelEvent.getModelIdentifier(item)
|
||||
val model = CustomItemModelEvent.getModelIdentifier(item) // TODO: remove global texture overrides, maybe
|
||||
if (model == null) {
|
||||
lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.modelid.fail"))
|
||||
return
|
||||
@@ -146,7 +149,7 @@ object PowerUserTools : FirmamentFeature {
|
||||
lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-profile"))
|
||||
return
|
||||
}
|
||||
val skullTexture = CustomSkyBlockTextures.getSkullTexture(profile)
|
||||
val skullTexture = getSkullId(profile)
|
||||
if (skullTexture == null) {
|
||||
lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-texture"))
|
||||
return
|
||||
@@ -179,7 +182,7 @@ object PowerUserTools : FirmamentFeature {
|
||||
MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail"))
|
||||
return
|
||||
}
|
||||
val id = CustomSkyBlockTextures.getSkullTexture(entity.owner!!)
|
||||
val id = getSkullId(entity.owner!!)
|
||||
if (id == null) {
|
||||
MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail"))
|
||||
} else {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.WrapperBakedModel as WrapperBakedModelFabric
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.WrapperBakedModel
|
||||
import moe.nea.firmament.util.ErrorUtil
|
||||
|
||||
interface BakedModelExtra {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun cast(originalModel: BakedModel): BakedModelExtra? {
|
||||
var p = originalModel
|
||||
for (i in 0..256) {
|
||||
p = when (p) {
|
||||
is BakedModelExtra -> return p
|
||||
is WrapperBakedModel -> p.wrapped
|
||||
is WrapperBakedModelFabric -> WrapperBakedModelFabric.unwrap(p)
|
||||
else -> break
|
||||
}
|
||||
}
|
||||
ErrorUtil.softError("Could not find a baked model for $originalModel")
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
var tintOverrides_firmament: TintOverrides?
|
||||
|
||||
fun getHeadModel_firmament(): BakedModel?
|
||||
fun setHeadModel_firmament(headModel: BakedModel?)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import net.minecraft.client.render.model.json.ModelOverrideList
|
||||
|
||||
interface BakedOverrideData {
|
||||
fun getFirmamentOverrides(): Array<FirmamentModelPredicate>?
|
||||
fun setFirmamentOverrides(overrides: Array<FirmamentModelPredicate>?)
|
||||
companion object{
|
||||
@Suppress("CAST_NEVER_SUCCEEDS")
|
||||
@JvmStatic
|
||||
fun cast(bakedOverride: ModelOverrideList.BakedOverride): BakedOverrideData = bakedOverride as BakedOverrideData
|
||||
}
|
||||
}
|
||||
@@ -1,301 +0,0 @@
|
||||
@file:UseSerializers(BlockPosSerializer::class, IdentifierSerializer::class)
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.fabricmc.loader.api.FabricLoader
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlinx.serialization.json.JsonDecoder
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.serializer
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.util.ModelIdentifier
|
||||
import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.resource.SinglePreparationResourceReloader
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.profiler.Profiler
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.BakeExtraModelsEvent
|
||||
import moe.nea.firmament.events.EarlyResourceReloadEvent
|
||||
import moe.nea.firmament.events.FinalizeResourceManagerEvent
|
||||
import moe.nea.firmament.events.SkyblockServerUpdateEvent
|
||||
import moe.nea.firmament.features.texturepack.CustomGlobalTextures.logger
|
||||
import moe.nea.firmament.util.IdentifierSerializer
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.SBData
|
||||
import moe.nea.firmament.util.SkyBlockIsland
|
||||
import moe.nea.firmament.util.json.BlockPosSerializer
|
||||
import moe.nea.firmament.util.json.SingletonSerializableList
|
||||
|
||||
|
||||
object CustomBlockTextures {
|
||||
@Serializable
|
||||
data class CustomBlockOverride(
|
||||
val modes: @Serializable(SingletonSerializableList::class) List<String>,
|
||||
val area: List<Area>? = null,
|
||||
val replacements: Map<Identifier, Replacement>,
|
||||
)
|
||||
|
||||
@Serializable(with = Replacement.Serializer::class)
|
||||
data class Replacement(
|
||||
val block: Identifier,
|
||||
val sound: Identifier?,
|
||||
) {
|
||||
|
||||
@Transient
|
||||
val blockModelIdentifier get() = ModelIdentifier(block.withPrefixedPath("block/"), "firmament")
|
||||
|
||||
@Transient
|
||||
val bakedModel: BakedModel by lazy(LazyThreadSafetyMode.NONE) {
|
||||
MC.instance.bakedModelManager.getModel(blockModelIdentifier)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@kotlinx.serialization.Serializer(Replacement::class)
|
||||
object DefaultSerializer : KSerializer<Replacement>
|
||||
|
||||
object Serializer : KSerializer<Replacement> {
|
||||
val delegate = serializer<JsonElement>()
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): Replacement {
|
||||
val jsonElement = decoder.decodeSerializableValue(delegate)
|
||||
if (jsonElement is JsonPrimitive) {
|
||||
require(jsonElement.isString)
|
||||
return Replacement(Identifier.tryParse(jsonElement.content)!!, null)
|
||||
}
|
||||
return (decoder as JsonDecoder).json.decodeFromJsonElement(DefaultSerializer, jsonElement)
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Replacement) {
|
||||
encoder.encodeSerializableValue(DefaultSerializer, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Area(
|
||||
val min: BlockPos,
|
||||
val max: BlockPos,
|
||||
) {
|
||||
@Transient
|
||||
val realMin = BlockPos(
|
||||
minOf(min.x, max.x),
|
||||
minOf(min.y, max.y),
|
||||
minOf(min.z, max.z),
|
||||
)
|
||||
|
||||
@Transient
|
||||
val realMax = BlockPos(
|
||||
maxOf(min.x, max.x),
|
||||
maxOf(min.y, max.y),
|
||||
maxOf(min.z, max.z),
|
||||
)
|
||||
|
||||
fun roughJoin(other: Area): Area {
|
||||
return Area(
|
||||
BlockPos(
|
||||
minOf(realMin.x, other.realMin.x),
|
||||
minOf(realMin.y, other.realMin.y),
|
||||
minOf(realMin.z, other.realMin.z),
|
||||
),
|
||||
BlockPos(
|
||||
maxOf(realMax.x, other.realMax.x),
|
||||
maxOf(realMax.y, other.realMax.y),
|
||||
maxOf(realMax.z, other.realMax.z),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun contains(blockPos: BlockPos): Boolean {
|
||||
return (blockPos.x in realMin.x..realMax.x) &&
|
||||
(blockPos.y in realMin.y..realMax.y) &&
|
||||
(blockPos.z in realMin.z..realMax.z)
|
||||
}
|
||||
}
|
||||
|
||||
data class LocationReplacements(
|
||||
val lookup: Map<Block, List<BlockReplacement>>
|
||||
)
|
||||
|
||||
data class BlockReplacement(
|
||||
val checks: List<Area>?,
|
||||
val replacement: Replacement,
|
||||
) {
|
||||
val roughCheck by lazy(LazyThreadSafetyMode.NONE) {
|
||||
if (checks == null || checks.size < 3) return@lazy null
|
||||
checks.reduce { acc, next -> acc.roughJoin(next) }
|
||||
}
|
||||
}
|
||||
|
||||
data class BakedReplacements(val data: Map<SkyBlockIsland, LocationReplacements>)
|
||||
|
||||
var allLocationReplacements: BakedReplacements = BakedReplacements(mapOf())
|
||||
var currentIslandReplacements: LocationReplacements? = null
|
||||
|
||||
fun refreshReplacements() {
|
||||
val location = SBData.skyblockLocation
|
||||
val replacements =
|
||||
if (CustomSkyBlockTextures.TConfig.enableBlockOverrides) location?.let(allLocationReplacements.data::get)
|
||||
else null
|
||||
val lastReplacements = currentIslandReplacements
|
||||
currentIslandReplacements = replacements
|
||||
if (lastReplacements != replacements) {
|
||||
MC.nextTick {
|
||||
MC.worldRenderer.chunks?.chunks?.forEach {
|
||||
// false schedules rebuilds outside a 27 block radius to happen async
|
||||
it.scheduleRebuild(false)
|
||||
}
|
||||
sodiumReloadTask?.run()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val sodiumReloadTask = runCatching {
|
||||
val r = Class.forName("moe.nea.firmament.compat.sodium.SodiumChunkReloader")
|
||||
.getConstructor()
|
||||
.newInstance() as Runnable
|
||||
r.run()
|
||||
r
|
||||
}.getOrElse {
|
||||
if (FabricLoader.getInstance().isModLoaded("sodium"))
|
||||
logger.error("Could not create sodium chunk reloader")
|
||||
null
|
||||
}
|
||||
|
||||
|
||||
fun matchesPosition(replacement: BlockReplacement, blockPos: BlockPos?): Boolean {
|
||||
if (blockPos == null) return true
|
||||
val rc = replacement.roughCheck
|
||||
if (rc != null && !rc.contains(blockPos)) return false
|
||||
val areas = replacement.checks
|
||||
if (areas != null && !areas.any { it.contains(blockPos) }) return false
|
||||
return true
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getReplacementModel(block: BlockState, blockPos: BlockPos?): BakedModel? {
|
||||
return getReplacement(block, blockPos)?.bakedModel
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getReplacement(block: BlockState, blockPos: BlockPos?): Replacement? {
|
||||
if (isInFallback() && blockPos == null) {
|
||||
return null
|
||||
}
|
||||
val replacements = currentIslandReplacements?.lookup?.get(block.block) ?: return null
|
||||
for (replacement in replacements) {
|
||||
if (replacement.checks == null || matchesPosition(replacement, blockPos))
|
||||
return replacement.replacement
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@Subscribe
|
||||
fun onLocation(event: SkyblockServerUpdateEvent) {
|
||||
refreshReplacements()
|
||||
}
|
||||
|
||||
@Volatile
|
||||
var preparationFuture: CompletableFuture<BakedReplacements> = CompletableFuture.completedFuture(BakedReplacements(
|
||||
mapOf()))
|
||||
|
||||
val insideFallbackCall = ThreadLocal.withInitial { 0 }
|
||||
|
||||
@JvmStatic
|
||||
fun enterFallbackCall() {
|
||||
insideFallbackCall.set(insideFallbackCall.get() + 1)
|
||||
}
|
||||
|
||||
fun isInFallback() = insideFallbackCall.get() > 0
|
||||
|
||||
@JvmStatic
|
||||
fun exitFallbackCall() {
|
||||
insideFallbackCall.set(insideFallbackCall.get() - 1)
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onEarlyReload(event: EarlyResourceReloadEvent) {
|
||||
preparationFuture = CompletableFuture
|
||||
.supplyAsync(
|
||||
{ prepare(event.resourceManager) }, event.preparationExecutor)
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun bakeExtraModels(event: BakeExtraModelsEvent) {
|
||||
preparationFuture.join().data.values
|
||||
.flatMap { it.lookup.values }
|
||||
.flatten()
|
||||
.mapTo(mutableSetOf()) { it.replacement.blockModelIdentifier }
|
||||
.forEach { event.addNonItemModel(it, it.id) }
|
||||
}
|
||||
|
||||
private fun prepare(manager: ResourceManager): BakedReplacements {
|
||||
val resources = manager.findResources("overrides/blocks") {
|
||||
it.namespace == "firmskyblock" && it.path.endsWith(".json")
|
||||
}
|
||||
val map = mutableMapOf<SkyBlockIsland, MutableMap<Block, MutableList<BlockReplacement>>>()
|
||||
for ((file, resource) in resources) {
|
||||
val json =
|
||||
Firmament.tryDecodeJsonFromStream<CustomBlockOverride>(resource.inputStream)
|
||||
.getOrElse { ex ->
|
||||
logger.error("Failed to load block texture override at $file", ex)
|
||||
continue
|
||||
}
|
||||
for (mode in json.modes) {
|
||||
val island = SkyBlockIsland.forMode(mode)
|
||||
val islandMpa = map.getOrPut(island, ::mutableMapOf)
|
||||
for ((blockId, replacement) in json.replacements) {
|
||||
val block = MC.defaultRegistries.getOrThrow(RegistryKeys.BLOCK)
|
||||
.getOptional(RegistryKey.of(RegistryKeys.BLOCK, blockId))
|
||||
.getOrNull()
|
||||
if (block == null) {
|
||||
logger.error("Failed to load block texture override at ${file}: unknown block '$blockId'")
|
||||
continue
|
||||
}
|
||||
val replacements = islandMpa.getOrPut(block.value(), ::mutableListOf)
|
||||
replacements.add(BlockReplacement(json.area, replacement))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BakedReplacements(map.mapValues { LocationReplacements(it.value) })
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun patchIndigo(orig: BakedModel, pos: BlockPos, state: BlockState): BakedModel {
|
||||
return getReplacementModel(state, pos) ?: orig
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onStart(event: FinalizeResourceManagerEvent) {
|
||||
event.resourceManager.registerReloader(object :
|
||||
SinglePreparationResourceReloader<BakedReplacements>() {
|
||||
override fun prepare(manager: ResourceManager, profiler: Profiler): BakedReplacements {
|
||||
return preparationFuture.join()
|
||||
}
|
||||
|
||||
override fun apply(prepared: BakedReplacements, manager: ResourceManager, profiler: Profiler?) {
|
||||
allLocationReplacements = prepared
|
||||
refreshReplacements()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
@file:UseSerializers(IdentifierSerializer::class)
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import java.util.Optional
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.equipment.EquipmentModel
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.resource.SinglePreparationResourceReloader
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.profiler.Profiler
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.FinalizeResourceManagerEvent
|
||||
import moe.nea.firmament.features.texturepack.CustomGlobalTextures.logger
|
||||
import moe.nea.firmament.util.IdentifierSerializer
|
||||
import moe.nea.firmament.util.collections.WeakCache
|
||||
import moe.nea.firmament.util.skyBlockId
|
||||
|
||||
object CustomGlobalArmorOverrides {
|
||||
@Serializable
|
||||
data class ArmorOverride(
|
||||
@SerialName("item_ids")
|
||||
val itemIds: List<String>,
|
||||
val layers: List<ArmorOverrideLayer>? = null,
|
||||
val model: Identifier? = null,
|
||||
val overrides: List<ArmorOverrideOverride> = listOf(),
|
||||
) {
|
||||
@Transient
|
||||
lateinit var modelIdentifier: Identifier
|
||||
fun bake() {
|
||||
modelIdentifier = bakeModel(model, layers)
|
||||
overrides.forEach { it.bake() }
|
||||
}
|
||||
|
||||
init {
|
||||
require(layers != null || model != null) { "Either model or layers must be specified for armor override" }
|
||||
require(layers == null || model == null) { "Can't specify both model and layers for armor override" }
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ArmorOverrideLayer(
|
||||
val tint: Boolean = false,
|
||||
val identifier: Identifier,
|
||||
val suffix: String = "",
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ArmorOverrideOverride(
|
||||
val predicate: FirmamentModelPredicate,
|
||||
val layers: List<ArmorOverrideLayer>? = null,
|
||||
val model: Identifier? = null,
|
||||
) {
|
||||
init {
|
||||
require(layers != null || model != null) { "Either model or layers must be specified for armor override override" }
|
||||
require(layers == null || model == null) { "Can't specify both model and layers for armor override override" }
|
||||
}
|
||||
|
||||
@Transient
|
||||
lateinit var modelIdentifier: Identifier
|
||||
fun bake() {
|
||||
modelIdentifier = bakeModel(model, layers)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val overrideCache = WeakCache.memoize<ItemStack, Optional<Identifier>>("ArmorOverrides") { stack ->
|
||||
val id = stack.skyBlockId ?: return@memoize Optional.empty()
|
||||
val override = overrides[id.neuItem] ?: return@memoize Optional.empty()
|
||||
for (suboverride in override.overrides) {
|
||||
if (suboverride.predicate.test(stack)) {
|
||||
return@memoize Optional.of(suboverride.modelIdentifier)
|
||||
}
|
||||
}
|
||||
return@memoize Optional.of(override.modelIdentifier)
|
||||
}
|
||||
|
||||
var overrides: Map<String, ArmorOverride> = mapOf()
|
||||
private var bakedOverrides: MutableMap<Identifier, EquipmentModel> = mutableMapOf()
|
||||
private val sentinelFirmRunning = AtomicInteger()
|
||||
|
||||
private fun bakeModel(model: Identifier?, layers: List<ArmorOverrideLayer>?): Identifier {
|
||||
require(model == null || layers == null)
|
||||
if (model != null) {
|
||||
return model
|
||||
} else if (layers != null) {
|
||||
val idNumber = sentinelFirmRunning.incrementAndGet()
|
||||
val identifier = Identifier.of("firmament:sentinel/$idNumber")
|
||||
val equipmentLayers = layers.map {
|
||||
EquipmentModel.Layer(
|
||||
it.identifier, if (it.tint) {
|
||||
Optional.of(EquipmentModel.Dyeable(Optional.empty()))
|
||||
} else {
|
||||
Optional.empty()
|
||||
},
|
||||
false
|
||||
)
|
||||
}
|
||||
bakedOverrides[identifier] = EquipmentModel(
|
||||
mapOf(
|
||||
EquipmentModel.LayerType.HUMANOID to equipmentLayers,
|
||||
EquipmentModel.LayerType.HUMANOID_LEGGINGS to equipmentLayers,
|
||||
)
|
||||
)
|
||||
return identifier
|
||||
} else {
|
||||
error("Either model or layers must be non null")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Subscribe
|
||||
fun onStart(event: FinalizeResourceManagerEvent) {
|
||||
event.resourceManager.registerReloader(object :
|
||||
SinglePreparationResourceReloader<Map<String, ArmorOverride>>() {
|
||||
override fun prepare(manager: ResourceManager, profiler: Profiler): Map<String, ArmorOverride> {
|
||||
val overrideFiles = manager.findResources("overrides/armor_models") {
|
||||
it.namespace == "firmskyblock" && it.path.endsWith(".json")
|
||||
}
|
||||
val overrides = overrideFiles.mapNotNull {
|
||||
Firmament.tryDecodeJsonFromStream<ArmorOverride>(it.value.inputStream).getOrElse { ex ->
|
||||
logger.error("Failed to load armor texture override at ${it.key}", ex)
|
||||
null
|
||||
}
|
||||
}
|
||||
val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } }
|
||||
.toMap()
|
||||
return associatedMap
|
||||
}
|
||||
|
||||
override fun apply(prepared: Map<String, ArmorOverride>, manager: ResourceManager, profiler: Profiler) {
|
||||
bakedOverrides.clear()
|
||||
prepared.forEach { it.value.bake() }
|
||||
overrides = prepared
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun overrideArmor(itemStack: ItemStack): Optional<Identifier> {
|
||||
return overrideCache.invoke(itemStack)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun overrideArmorLayer(id: Identifier): EquipmentModel? {
|
||||
return bakedOverrides[id]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
@file:UseSerializers(IdentifierSerializer::class, CustomModelOverrideParser.FirmamentRootPredicateSerializer::class)
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
|
||||
import java.util.Optional
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import org.slf4j.LoggerFactory
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.client.render.item.ItemModels
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.util.ModelIdentifier
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.resource.SinglePreparationResourceReloader
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.profiler.Profiler
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.BakeExtraModelsEvent
|
||||
import moe.nea.firmament.events.EarlyResourceReloadEvent
|
||||
import moe.nea.firmament.events.FinalizeResourceManagerEvent
|
||||
import moe.nea.firmament.events.ScreenChangeEvent
|
||||
import moe.nea.firmament.events.subscription.SubscriptionOwner
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
import moe.nea.firmament.util.IdentifierSerializer
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.collections.WeakCache
|
||||
import moe.nea.firmament.util.intoOptional
|
||||
import moe.nea.firmament.util.json.SingletonSerializableList
|
||||
import moe.nea.firmament.util.runNull
|
||||
|
||||
object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalTextures.CustomGuiTextureOverride>(),
|
||||
SubscriptionOwner {
|
||||
override val delegateFeature: FirmamentFeature
|
||||
get() = CustomSkyBlockTextures
|
||||
|
||||
class CustomGuiTextureOverride(
|
||||
val classes: List<ItemOverrideCollection>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GlobalItemOverride(
|
||||
val screen: @Serializable(SingletonSerializableList::class) List<Identifier>,
|
||||
val model: Identifier,
|
||||
val predicate: FirmamentModelPredicate,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ScreenFilter(
|
||||
val title: StringMatcher,
|
||||
)
|
||||
|
||||
data class ItemOverrideCollection(
|
||||
val screenFilter: ScreenFilter,
|
||||
val overrides: List<GlobalItemOverride>,
|
||||
)
|
||||
|
||||
@Subscribe
|
||||
fun onStart(event: FinalizeResourceManagerEvent) {
|
||||
MC.resourceManager.registerReloader(this)
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onEarlyReload(event: EarlyResourceReloadEvent) {
|
||||
preparationFuture = CompletableFuture
|
||||
.supplyAsync(
|
||||
{
|
||||
prepare(event.resourceManager)
|
||||
}, event.preparationExecutor)
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onBakeModels(event: BakeExtraModelsEvent) {
|
||||
for (guiClassOverride in preparationFuture.join().classes) {
|
||||
for (override in guiClassOverride.overrides) {
|
||||
event.addItemModel(ModelIdentifier(override.model, "inventory"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Volatile
|
||||
var preparationFuture: CompletableFuture<CustomGuiTextureOverride> = CompletableFuture.completedFuture(
|
||||
CustomGuiTextureOverride(listOf()))
|
||||
|
||||
override fun prepare(manager: ResourceManager?, profiler: Profiler?): CustomGuiTextureOverride {
|
||||
return preparationFuture.join()
|
||||
}
|
||||
|
||||
override fun apply(prepared: CustomGuiTextureOverride, manager: ResourceManager?, profiler: Profiler?) {
|
||||
this.guiClassOverrides = prepared
|
||||
}
|
||||
|
||||
val logger = LoggerFactory.getLogger(CustomGlobalTextures::class.java)
|
||||
fun prepare(manager: ResourceManager): CustomGuiTextureOverride {
|
||||
val overrideResources =
|
||||
manager.findResources("overrides/item") { it.namespace == "firmskyblock" && it.path.endsWith(".json") }
|
||||
.mapNotNull {
|
||||
Firmament.tryDecodeJsonFromStream<GlobalItemOverride>(it.value.inputStream).getOrElse { ex ->
|
||||
logger.error("Failed to load global item override at ${it.key}", ex)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val byGuiClass = overrideResources.flatMap { override -> override.screen.toSet().map { it to override } }
|
||||
.groupBy { it.first }
|
||||
val guiClasses = byGuiClass.entries
|
||||
.mapNotNull {
|
||||
val key = it.key
|
||||
val guiClassResource =
|
||||
manager.getResource(Identifier.of(key.namespace, "filters/screen/${key.path}.json"))
|
||||
.getOrNull()
|
||||
?: return@mapNotNull runNull {
|
||||
logger.error("Failed to locate screen filter at $key")
|
||||
}
|
||||
val screenFilter =
|
||||
Firmament.tryDecodeJsonFromStream<ScreenFilter>(guiClassResource.inputStream)
|
||||
.getOrElse { ex ->
|
||||
logger.error("Failed to load screen filter at $key", ex)
|
||||
return@mapNotNull null
|
||||
}
|
||||
ItemOverrideCollection(screenFilter, it.value.map { it.second })
|
||||
}
|
||||
logger.info("Loaded ${overrideResources.size} global item overrides")
|
||||
return CustomGuiTextureOverride(guiClasses)
|
||||
}
|
||||
|
||||
var guiClassOverrides = CustomGuiTextureOverride(listOf())
|
||||
|
||||
var matchingOverrides: Set<ItemOverrideCollection> = setOf()
|
||||
|
||||
@Subscribe
|
||||
fun onOpenGui(event: ScreenChangeEvent) {
|
||||
val newTitle = event.new?.title ?: Text.empty()
|
||||
matchingOverrides = guiClassOverrides.classes
|
||||
.filterTo(mutableSetOf()) { it.screenFilter.title.matches(newTitle) }
|
||||
}
|
||||
|
||||
val overrideCache =
|
||||
WeakCache.memoize<ItemStack, ItemModels, Optional<BakedModel>>("CustomGlobalTextureModelOverrides") { stack, models ->
|
||||
matchingOverrides
|
||||
.firstNotNullOfOrNull {
|
||||
it.overrides
|
||||
.asSequence()
|
||||
.filter { it.predicate.test(stack) }
|
||||
.map { models.getModel(it.model) }
|
||||
.firstOrNull()
|
||||
}
|
||||
.intoOptional()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun replaceGlobalModel(
|
||||
models: ItemModels,
|
||||
stack: ItemStack,
|
||||
): BakedModel? {
|
||||
return overrideCache.invoke(stack, models).getOrNull()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import moe.nea.firmament.features.texturepack.predicates.AndPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.DisplayNamePredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.ExtraAttributesPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.ItemPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.LorePredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.NotPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.OrPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.PetPredicate
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
object CustomModelOverrideParser {
|
||||
object FirmamentRootPredicateSerializer : KSerializer<FirmamentModelPredicate> {
|
||||
val delegateSerializer = kotlinx.serialization.json.JsonObject.serializer()
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = SerialDescriptor("FirmamentModelRootPredicate", delegateSerializer.descriptor)
|
||||
|
||||
override fun deserialize(decoder: Decoder): FirmamentModelPredicate {
|
||||
val json = decoder.decodeSerializableValue(delegateSerializer).intoGson() as JsonObject
|
||||
return AndPredicate(parsePredicates(json).toTypedArray())
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: FirmamentModelPredicate) {
|
||||
TODO("Cannot serialize firmament predicates")
|
||||
}
|
||||
}
|
||||
|
||||
val predicateParsers = mutableMapOf<Identifier, FirmamentModelPredicateParser>()
|
||||
|
||||
|
||||
fun registerPredicateParser(name: String, parser: FirmamentModelPredicateParser) {
|
||||
predicateParsers[Identifier.of("firmament", name)] = parser
|
||||
}
|
||||
|
||||
init {
|
||||
registerPredicateParser("display_name", DisplayNamePredicate.Parser)
|
||||
registerPredicateParser("lore", LorePredicate.Parser)
|
||||
registerPredicateParser("all", AndPredicate.Parser)
|
||||
registerPredicateParser("any", OrPredicate.Parser)
|
||||
registerPredicateParser("not", NotPredicate.Parser)
|
||||
registerPredicateParser("item", ItemPredicate.Parser)
|
||||
registerPredicateParser("extra_attributes", ExtraAttributesPredicate.Parser)
|
||||
registerPredicateParser("pet", PetPredicate.Parser)
|
||||
}
|
||||
|
||||
private val neverPredicate = listOf(
|
||||
object : FirmamentModelPredicate {
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
fun parsePredicates(predicates: JsonObject): List<FirmamentModelPredicate> {
|
||||
val parsedPredicates = mutableListOf<FirmamentModelPredicate>()
|
||||
for (predicateName in predicates.keySet()) {
|
||||
if (!predicateName.startsWith("firmament:")) continue
|
||||
val identifier = Identifier.of(predicateName)
|
||||
val parser = predicateParsers[identifier] ?: return neverPredicate
|
||||
val parsedPredicate = parser.parse(predicates[predicateName]) ?: return neverPredicate
|
||||
parsedPredicates.add(parsedPredicate)
|
||||
}
|
||||
return parsedPredicates
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseCustomModelOverrides(jsonObject: JsonObject): Array<FirmamentModelPredicate>? {
|
||||
val predicates = (jsonObject["predicate"] as? JsonObject) ?: return null
|
||||
val parsedPredicates = parsePredicates(predicates)
|
||||
if (parsedPredicates.isEmpty())
|
||||
return null
|
||||
return parsedPredicates.toTypedArray()
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture
|
||||
import com.mojang.authlib.properties.Property
|
||||
import java.util.Optional
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.block.SkullBlock
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.util.ModelIdentifier
|
||||
import net.minecraft.component.type.ProfileComponent
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.BakeExtraModelsEvent
|
||||
import moe.nea.firmament.events.CustomItemModelEvent
|
||||
import moe.nea.firmament.events.FinalizeResourceManagerEvent
|
||||
import moe.nea.firmament.events.TickEvent
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
import moe.nea.firmament.gui.config.ManagedConfig
|
||||
import moe.nea.firmament.util.collections.WeakCache
|
||||
import moe.nea.firmament.util.mc.decodeProfileTextureProperty
|
||||
import moe.nea.firmament.util.skyBlockId
|
||||
|
||||
object CustomSkyBlockTextures : FirmamentFeature {
|
||||
override val identifier: String
|
||||
get() = "custom-skyblock-textures"
|
||||
|
||||
object TConfig : ManagedConfig(identifier, Category.INTEGRATIONS) { // TODO: should this be its own thing?
|
||||
val enabled by toggle("enabled") { true }
|
||||
val skullsEnabled by toggle("skulls-enabled") { true }
|
||||
val cacheForever by toggle("cache-forever") { true }
|
||||
val cacheDuration by integer("cache-duration", 0, 100) { 1 }
|
||||
val enableModelOverrides by toggle("model-overrides") { true }
|
||||
val enableArmorOverrides by toggle("armor-overrides") { true }
|
||||
val enableBlockOverrides by toggle("block-overrides") { true }
|
||||
val enableLegacyCIT by toggle("legacy-cit") { true }
|
||||
val allowRecoloringUiText by toggle("recolor-text") { true }
|
||||
}
|
||||
|
||||
override val config: ManagedConfig
|
||||
get() = TConfig
|
||||
|
||||
val allItemCaches by lazy {
|
||||
listOf(
|
||||
CustomItemModelEvent.cache.cache,
|
||||
skullTextureCache.cache,
|
||||
CustomGlobalTextures.overrideCache.cache,
|
||||
CustomGlobalArmorOverrides.overrideCache.cache
|
||||
)
|
||||
}
|
||||
|
||||
fun clearAllCaches() {
|
||||
allItemCaches.forEach(WeakCache<*, *, *>::clear)
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onTick(it: TickEvent) {
|
||||
if (TConfig.cacheForever) return
|
||||
if (TConfig.cacheDuration < 1 || it.tickCount % TConfig.cacheDuration == 0) {
|
||||
clearAllCaches()
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onStart(event: FinalizeResourceManagerEvent) {
|
||||
event.registerOnApply("Clear firmament CIT caches") {
|
||||
clearAllCaches()
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun bakeCustomFirmModels(event: BakeExtraModelsEvent) {
|
||||
val resources =
|
||||
MinecraftClient.getInstance().resourceManager.findResources("models/item"
|
||||
) { it: Identifier ->
|
||||
"firmskyblock" == it.namespace && it.path
|
||||
.endsWith(".json")
|
||||
}
|
||||
for (identifier in resources.keys) {
|
||||
val modelId = ModelIdentifier.ofInventoryVariant(
|
||||
Identifier.of(
|
||||
"firmskyblock",
|
||||
identifier.path.substring(
|
||||
"models/item/".length,
|
||||
identifier.path.length - ".json".length),
|
||||
))
|
||||
event.addItemModel(modelId)
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onCustomModelId(it: CustomItemModelEvent) {
|
||||
if (!TConfig.enabled) return
|
||||
val id = it.itemStack.skyBlockId ?: return
|
||||
it.overrideModel = ModelIdentifier.ofInventoryVariant(Identifier.of("firmskyblock", id.identifier.path))
|
||||
}
|
||||
|
||||
private val skullTextureCache =
|
||||
WeakCache.memoize<ProfileComponent, Optional<Identifier>>("SkullTextureCache") { component ->
|
||||
val id = getSkullTexture(component) ?: return@memoize Optional.empty()
|
||||
if (!MinecraftClient.getInstance().resourceManager.getResource(id).isPresent) {
|
||||
return@memoize Optional.empty()
|
||||
}
|
||||
return@memoize Optional.of(id)
|
||||
}
|
||||
|
||||
private val mcUrlRegex = "https?://textures.minecraft.net/texture/([a-fA-F0-9]+)".toRegex()
|
||||
|
||||
fun getSkullId(textureProperty: Property): String? {
|
||||
val texture = decodeProfileTextureProperty(textureProperty) ?: return null
|
||||
val textureUrl =
|
||||
texture.textures[MinecraftProfileTexture.Type.SKIN]?.url ?: return null
|
||||
val mcUrlData = mcUrlRegex.matchEntire(textureUrl) ?: return null
|
||||
return mcUrlData.groupValues[1]
|
||||
}
|
||||
|
||||
fun getSkullTexture(profile: ProfileComponent): Identifier? {
|
||||
val id = getSkullId(profile.properties["textures"].firstOrNull() ?: return null) ?: return null
|
||||
return Identifier.of("firmskyblock", "textures/placedskull/$id.png")
|
||||
}
|
||||
|
||||
fun modifySkullTexture(
|
||||
type: SkullBlock.SkullType?,
|
||||
component: ProfileComponent?,
|
||||
cir: CallbackInfoReturnable<RenderLayer>
|
||||
) {
|
||||
if (type != SkullBlock.Type.PLAYER) return
|
||||
if (!TConfig.skullsEnabled) return
|
||||
if (component == null) return
|
||||
|
||||
val n = skullTextureCache.invoke(component).getOrNull() ?: return
|
||||
cir.returnValue = RenderLayer.getEntityTranslucent(n)
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import java.util.Optional
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.resource.SinglePreparationResourceReloader
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.profiler.Profiler
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.FinalizeResourceManagerEvent
|
||||
import moe.nea.firmament.util.collections.WeakCache
|
||||
|
||||
object CustomTextColors : SinglePreparationResourceReloader<CustomTextColors.TextOverrides?>() {
|
||||
@Serializable
|
||||
data class TextOverrides(
|
||||
val defaultColor: Int,
|
||||
val overrides: List<TextOverride> = listOf()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class TextOverride(
|
||||
val predicate: StringMatcher,
|
||||
val override: Int,
|
||||
)
|
||||
|
||||
@Subscribe
|
||||
fun registerTextColorReloader(event: FinalizeResourceManagerEvent) {
|
||||
event.resourceManager.registerReloader(this)
|
||||
}
|
||||
|
||||
val cache = WeakCache.memoize<Text, Optional<Int>>("CustomTextColor") { text ->
|
||||
val override = textOverrides ?: return@memoize Optional.empty()
|
||||
Optional.of(override.overrides.find { it.predicate.matches(text) }?.override ?: override.defaultColor)
|
||||
}
|
||||
|
||||
fun mapTextColor(text: Text, oldColor: Int): Int {
|
||||
if (textOverrides == null) return oldColor
|
||||
return cache(text).getOrNull() ?: oldColor
|
||||
}
|
||||
|
||||
override fun prepare(
|
||||
manager: ResourceManager,
|
||||
profiler: Profiler
|
||||
): TextOverrides? {
|
||||
val resource = manager.getResource(Identifier.of("firmskyblock", "overrides/text_colors.json")).getOrNull()
|
||||
?: return null
|
||||
return Firmament.tryDecodeJsonFromStream<TextOverrides>(resource.inputStream)
|
||||
.getOrElse {
|
||||
Firmament.logger.error("Could not parse text_colors.json", it)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
var textOverrides: TextOverrides? = null
|
||||
|
||||
override fun apply(
|
||||
prepared: TextOverrides?,
|
||||
manager: ResourceManager,
|
||||
profiler: Profiler
|
||||
) {
|
||||
textOverrides = prepared
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import net.minecraft.item.ItemStack
|
||||
|
||||
interface FirmamentModelPredicate {
|
||||
fun test(stack: ItemStack): Boolean
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
|
||||
interface FirmamentModelPredicateParser {
|
||||
fun parse(jsonElement: JsonElement): FirmamentModelPredicate?
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import net.minecraft.client.render.model.Baker
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
interface JsonUnbakedModelFirmExtra {
|
||||
fun storeExtraBaker_firmament(baker: Baker)
|
||||
|
||||
fun setHeadModel_firmament(identifier: Identifier?)
|
||||
fun getHeadModel_firmament(): Identifier?
|
||||
|
||||
fun setTintOverrides_firmament(tintOverrides: TintOverrides?)
|
||||
fun getTintOverrides_firmament(): TintOverrides
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import net.minecraft.client.render.model.json.ModelOverride
|
||||
|
||||
interface ModelOverrideData {
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
@Suppress("CAST_NEVER_SUCCEEDS")
|
||||
fun cast(override: ModelOverride) = override as ModelOverrideData
|
||||
}
|
||||
|
||||
fun getFirmamentOverrides(): Array<FirmamentModelPredicate>?
|
||||
fun setFirmamentOverrides(overrides: Array<FirmamentModelPredicate>?)
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import io.github.moulberry.repo.data.Rarity
|
||||
import moe.nea.firmament.util.useMatch
|
||||
|
||||
abstract class RarityMatcher {
|
||||
abstract fun match(rarity: Rarity): Boolean
|
||||
|
||||
companion object {
|
||||
fun parse(jsonElement: JsonElement): RarityMatcher {
|
||||
val string = jsonElement.asString
|
||||
val range = parseRange(string)
|
||||
if (range != null) return range
|
||||
return Exact(Rarity.valueOf(string))
|
||||
}
|
||||
|
||||
private val allRarities = Rarity.entries.joinToString("|", "(?:", ")")
|
||||
private val intervalSpec =
|
||||
"(?<beginningOpen>[\\[\\(])(?<beginning>$allRarities)?,(?<ending>$allRarities)?(?<endingOpen>[\\]\\)])"
|
||||
.toPattern()
|
||||
|
||||
fun parseRange(string: String): RangeMatcher? {
|
||||
intervalSpec.useMatch<Nothing>(string) {
|
||||
// Open in the set-theory sense, meaning does not include its end.
|
||||
val beginningOpen = group("beginningOpen") == "("
|
||||
val endingOpen = group("endingOpen") == ")"
|
||||
val beginning = group("beginning")?.let(Rarity::valueOf)
|
||||
val ending = group("ending")?.let(Rarity::valueOf)
|
||||
return RangeMatcher(beginning, !beginningOpen, ending, !endingOpen)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class Exact(val expected: Rarity) : RarityMatcher() {
|
||||
override fun match(rarity: Rarity): Boolean {
|
||||
return rarity == expected
|
||||
}
|
||||
}
|
||||
|
||||
data class RangeMatcher(
|
||||
val beginning: Rarity?,
|
||||
val beginningInclusive: Boolean,
|
||||
val ending: Rarity?,
|
||||
val endingInclusive: Boolean,
|
||||
) : RarityMatcher() {
|
||||
override fun match(rarity: Rarity): Boolean {
|
||||
if (beginning != null) {
|
||||
if (beginningInclusive) {
|
||||
if (rarity < beginning) return false
|
||||
} else {
|
||||
if (rarity <= beginning) return false
|
||||
}
|
||||
}
|
||||
if (ending != null) {
|
||||
if (endingInclusive) {
|
||||
if (rarity > ending) return false
|
||||
} else {
|
||||
if (rarity >= ending) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonNull
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import com.google.gson.internal.LazilyParsedNumber
|
||||
import java.util.function.Predicate
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import net.minecraft.nbt.NbtString
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.removeColorCodes
|
||||
|
||||
@Serializable(with = StringMatcher.Serializer::class)
|
||||
interface StringMatcher {
|
||||
fun matches(string: String): Boolean
|
||||
fun matches(text: Text): Boolean {
|
||||
return matches(text.string)
|
||||
}
|
||||
|
||||
fun matches(nbt: NbtString): Boolean {
|
||||
val string = nbt.asString()
|
||||
val jsonStart = string.indexOf('{')
|
||||
val stringStart = string.indexOf('"')
|
||||
val isString = stringStart >= 0 && string.subSequence(0, stringStart).isBlank()
|
||||
val isJson = jsonStart >= 0 && string.subSequence(0, jsonStart).isBlank()
|
||||
if (isString || isJson)
|
||||
return matches(Text.Serialization.fromJson(string, MC.defaultRegistries) ?: return false)
|
||||
return matches(string)
|
||||
}
|
||||
|
||||
class Equals(input: String, val stripColorCodes: Boolean) : StringMatcher {
|
||||
private val expected = if (stripColorCodes) input.removeColorCodes() else input
|
||||
override fun matches(string: String): Boolean {
|
||||
return expected == (if (stripColorCodes) string.removeColorCodes() else string)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Equals($expected, stripColorCodes = $stripColorCodes)"
|
||||
}
|
||||
}
|
||||
|
||||
class Pattern(val patternWithColorCodes: String, val stripColorCodes: Boolean) : StringMatcher {
|
||||
private val regex: Predicate<String> = patternWithColorCodes.toPattern().asMatchPredicate()
|
||||
override fun matches(string: String): Boolean {
|
||||
return regex.test(if (stripColorCodes) string.removeColorCodes() else string)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Pattern($patternWithColorCodes, stripColorCodes = $stripColorCodes)"
|
||||
}
|
||||
}
|
||||
|
||||
object Serializer : KSerializer<StringMatcher> {
|
||||
val delegateSerializer = kotlinx.serialization.json.JsonElement.serializer()
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = SerialDescriptor("StringMatcher", delegateSerializer.descriptor)
|
||||
|
||||
override fun deserialize(decoder: Decoder): StringMatcher {
|
||||
val delegate = decoder.decodeSerializableValue(delegateSerializer)
|
||||
val gsonDelegate = delegate.intoGson()
|
||||
return parse(gsonDelegate)
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: StringMatcher) {
|
||||
encoder.encodeSerializableValue(delegateSerializer, Companion.serialize(value).intoKotlinJson())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun serialize(stringMatcher: StringMatcher): JsonElement {
|
||||
TODO("Cannot serialize string matchers rn")
|
||||
}
|
||||
|
||||
fun parse(jsonElement: JsonElement): StringMatcher {
|
||||
if (jsonElement is JsonPrimitive) {
|
||||
return Equals(jsonElement.asString, true)
|
||||
}
|
||||
if (jsonElement is JsonObject) {
|
||||
val regex = jsonElement["regex"] as JsonPrimitive?
|
||||
val text = jsonElement["equals"] as JsonPrimitive?
|
||||
val shouldStripColor = when (val color = (jsonElement["color"] as JsonPrimitive?)?.asString) {
|
||||
"preserve" -> false
|
||||
"strip", null -> true
|
||||
else -> error("Unknown color preservation mode: $color")
|
||||
}
|
||||
if ((regex == null) == (text == null)) error("Could not parse $jsonElement as string matcher")
|
||||
if (regex != null)
|
||||
return Pattern(regex.asString, shouldStripColor)
|
||||
if (text != null)
|
||||
return Equals(text.asString, shouldStripColor)
|
||||
}
|
||||
error("Could not parse $jsonElement as a string matcher")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun JsonElement.intoKotlinJson(): kotlinx.serialization.json.JsonElement {
|
||||
when (this) {
|
||||
is JsonNull -> return kotlinx.serialization.json.JsonNull
|
||||
is JsonObject -> {
|
||||
return kotlinx.serialization.json.JsonObject(this.entrySet()
|
||||
.associate { it.key to it.value.intoKotlinJson() })
|
||||
}
|
||||
|
||||
is JsonArray -> {
|
||||
return kotlinx.serialization.json.JsonArray(this.map { it.intoKotlinJson() })
|
||||
}
|
||||
|
||||
is JsonPrimitive -> {
|
||||
if (this.isString)
|
||||
return kotlinx.serialization.json.JsonPrimitive(this.asString)
|
||||
if (this.isBoolean)
|
||||
return kotlinx.serialization.json.JsonPrimitive(this.asBoolean)
|
||||
return kotlinx.serialization.json.JsonPrimitive(this.asNumber)
|
||||
}
|
||||
|
||||
else -> error("Unknown json variant $this")
|
||||
}
|
||||
}
|
||||
|
||||
fun kotlinx.serialization.json.JsonElement.intoGson(): JsonElement {
|
||||
when (this) {
|
||||
is kotlinx.serialization.json.JsonNull -> return JsonNull.INSTANCE
|
||||
is kotlinx.serialization.json.JsonPrimitive -> {
|
||||
if (this.isString)
|
||||
return JsonPrimitive(this.content)
|
||||
if (this.content == "true")
|
||||
return JsonPrimitive(true)
|
||||
if (this.content == "false")
|
||||
return JsonPrimitive(false)
|
||||
return JsonPrimitive(LazilyParsedNumber(this.content))
|
||||
}
|
||||
|
||||
is kotlinx.serialization.json.JsonObject -> {
|
||||
val obj = JsonObject()
|
||||
for ((k, v) in this) {
|
||||
obj.add(k, v.intoGson())
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
is kotlinx.serialization.json.JsonArray -> {
|
||||
val arr = JsonArray()
|
||||
for (v in this) {
|
||||
arr.add(v.intoGson())
|
||||
}
|
||||
return arr
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import moe.nea.firmament.util.ErrorUtil
|
||||
|
||||
data class TintOverrides(
|
||||
val layerMap: Map<Int, TintOverride> = mapOf()
|
||||
) {
|
||||
val hasOverrides by lazy { layerMap.values.any { it !is Reset } }
|
||||
|
||||
companion object {
|
||||
val EMPTY = TintOverrides()
|
||||
private val threadLocal = object : ThreadLocal<TintOverrides>() {}
|
||||
fun enter(overrides: TintOverrides?) {
|
||||
ErrorUtil.softCheck("Double entered tintOverrides",
|
||||
threadLocal.get() == null)
|
||||
threadLocal.set(overrides ?: EMPTY)
|
||||
}
|
||||
|
||||
fun exit(overrides: TintOverrides?) {
|
||||
ErrorUtil.softCheck("Exited with non matching enter tintOverrides",
|
||||
threadLocal.get() == (overrides ?: EMPTY))
|
||||
threadLocal.remove()
|
||||
}
|
||||
|
||||
fun getCurrentOverrides(): TintOverrides {
|
||||
return ErrorUtil.notNullOr(threadLocal.get(), "Got current tintOverrides without entering") {
|
||||
EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
fun parse(jsonObject: JsonObject): TintOverrides {
|
||||
val map = mutableMapOf<Int, TintOverride>()
|
||||
for ((key, value) in jsonObject.entrySet()) {
|
||||
val layerIndex =
|
||||
ErrorUtil.notNullOr(key.toIntOrNull(),
|
||||
"Unknown layer index $value. Should be integer") { continue }
|
||||
if (value.isJsonNull) {
|
||||
map[layerIndex] = Reset
|
||||
continue
|
||||
}
|
||||
val override = (value as? JsonPrimitive)
|
||||
?.takeIf(JsonPrimitive::isNumber)
|
||||
?.asInt
|
||||
?.let(::Fixed)
|
||||
if (override == null) {
|
||||
ErrorUtil.softError("Invalid tint override for a layer: $value")
|
||||
continue
|
||||
}
|
||||
map[layerIndex] = override
|
||||
}
|
||||
return TintOverrides(map)
|
||||
}
|
||||
}
|
||||
|
||||
fun mergeWithParent(parent: TintOverrides): TintOverrides {
|
||||
val mergedMap = parent.layerMap.toMutableMap()
|
||||
mergedMap.putAll(this.layerMap)
|
||||
return TintOverrides(mergedMap)
|
||||
}
|
||||
|
||||
fun hasOverrides(): Boolean = hasOverrides
|
||||
fun getOverride(tintIndex: Int): Int? {
|
||||
return when (val tint = layerMap[tintIndex]) {
|
||||
is Reset -> null
|
||||
is Fixed -> tint.color
|
||||
null -> null
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface TintOverride
|
||||
data object Reset : TintOverride
|
||||
data class Fixed(val color: Int) : TintOverride
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack.predicates
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicateParser
|
||||
import net.minecraft.item.ItemStack
|
||||
|
||||
object AlwaysPredicate : FirmamentModelPredicate {
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
object Parser : FirmamentModelPredicateParser {
|
||||
override fun parse(jsonElement: JsonElement): FirmamentModelPredicate {
|
||||
return AlwaysPredicate
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package moe.nea.firmament.features.texturepack.predicates
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import moe.nea.firmament.features.texturepack.CustomModelOverrideParser
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicateParser
|
||||
import net.minecraft.item.ItemStack
|
||||
|
||||
class AndPredicate(val children: Array<FirmamentModelPredicate>) : FirmamentModelPredicate {
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
return children.all { it.test(stack) }
|
||||
}
|
||||
|
||||
object Parser : FirmamentModelPredicateParser {
|
||||
override fun parse(jsonElement: JsonElement): FirmamentModelPredicate {
|
||||
val children =
|
||||
(jsonElement as JsonArray)
|
||||
.flatMap {
|
||||
CustomModelOverrideParser.parsePredicates(it as JsonObject)
|
||||
}
|
||||
.toTypedArray()
|
||||
return AndPredicate(children)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack.predicates
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicateParser
|
||||
import moe.nea.firmament.features.texturepack.StringMatcher
|
||||
import net.minecraft.item.ItemStack
|
||||
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
||||
|
||||
data class DisplayNamePredicate(val stringMatcher: StringMatcher) : FirmamentModelPredicate {
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
val display = stack.displayNameAccordingToNbt
|
||||
return stringMatcher.matches(display)
|
||||
}
|
||||
|
||||
object Parser : FirmamentModelPredicateParser {
|
||||
override fun parse(jsonElement: JsonElement): FirmamentModelPredicate {
|
||||
return DisplayNamePredicate(StringMatcher.parse(jsonElement))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,271 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack.predicates
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicateParser
|
||||
import moe.nea.firmament.features.texturepack.StringMatcher
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.NbtByte
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.nbt.NbtDouble
|
||||
import net.minecraft.nbt.NbtElement
|
||||
import net.minecraft.nbt.NbtFloat
|
||||
import net.minecraft.nbt.NbtInt
|
||||
import net.minecraft.nbt.NbtList
|
||||
import net.minecraft.nbt.NbtLong
|
||||
import net.minecraft.nbt.NbtShort
|
||||
import net.minecraft.nbt.NbtString
|
||||
import moe.nea.firmament.util.extraAttributes
|
||||
|
||||
fun interface NbtMatcher {
|
||||
fun matches(nbt: NbtElement): Boolean
|
||||
|
||||
object Parser {
|
||||
fun parse(jsonElement: JsonElement): NbtMatcher? {
|
||||
if (jsonElement is JsonPrimitive) {
|
||||
if (jsonElement.isString) {
|
||||
val string = jsonElement.asString
|
||||
return MatchStringExact(string)
|
||||
}
|
||||
if (jsonElement.isNumber) {
|
||||
return MatchNumberExact(jsonElement.asLong) //TODO: parse generic number
|
||||
}
|
||||
}
|
||||
if (jsonElement is JsonObject) {
|
||||
var encounteredParser: NbtMatcher? = null
|
||||
for (entry in ExclusiveParserType.entries) {
|
||||
val data = jsonElement[entry.key] ?: continue
|
||||
if (encounteredParser != null) {
|
||||
// TODO: warn
|
||||
return null
|
||||
}
|
||||
encounteredParser = entry.parse(data) ?: return null
|
||||
}
|
||||
return encounteredParser
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
enum class ExclusiveParserType(val key: String) {
|
||||
STRING("string") {
|
||||
override fun parse(element: JsonElement): NbtMatcher? {
|
||||
return MatchString(StringMatcher.parse(element))
|
||||
}
|
||||
},
|
||||
INT("int") {
|
||||
override fun parse(element: JsonElement): NbtMatcher? {
|
||||
return parseGenericNumber(element,
|
||||
{ it.asInt },
|
||||
{ (it as? NbtInt)?.intValue() },
|
||||
{ a, b ->
|
||||
if (a == b) Comparison.EQUAL
|
||||
else if (a < b) Comparison.LESS_THAN
|
||||
else Comparison.GREATER
|
||||
})
|
||||
}
|
||||
},
|
||||
FLOAT("float") {
|
||||
override fun parse(element: JsonElement): NbtMatcher? {
|
||||
return parseGenericNumber(element,
|
||||
{ it.asFloat },
|
||||
{ (it as? NbtFloat)?.floatValue() },
|
||||
{ a, b ->
|
||||
if (a == b) Comparison.EQUAL
|
||||
else if (a < b) Comparison.LESS_THAN
|
||||
else Comparison.GREATER
|
||||
})
|
||||
}
|
||||
},
|
||||
DOUBLE("double") {
|
||||
override fun parse(element: JsonElement): NbtMatcher? {
|
||||
return parseGenericNumber(element,
|
||||
{ it.asDouble },
|
||||
{ (it as? NbtDouble)?.doubleValue() },
|
||||
{ a, b ->
|
||||
if (a == b) Comparison.EQUAL
|
||||
else if (a < b) Comparison.LESS_THAN
|
||||
else Comparison.GREATER
|
||||
})
|
||||
}
|
||||
},
|
||||
LONG("long") {
|
||||
override fun parse(element: JsonElement): NbtMatcher? {
|
||||
return parseGenericNumber(element,
|
||||
{ it.asLong },
|
||||
{ (it as? NbtLong)?.longValue() },
|
||||
{ a, b ->
|
||||
if (a == b) Comparison.EQUAL
|
||||
else if (a < b) Comparison.LESS_THAN
|
||||
else Comparison.GREATER
|
||||
})
|
||||
}
|
||||
},
|
||||
SHORT("short") {
|
||||
override fun parse(element: JsonElement): NbtMatcher? {
|
||||
return parseGenericNumber(element,
|
||||
{ it.asShort },
|
||||
{ (it as? NbtShort)?.shortValue() },
|
||||
{ a, b ->
|
||||
if (a == b) Comparison.EQUAL
|
||||
else if (a < b) Comparison.LESS_THAN
|
||||
else Comparison.GREATER
|
||||
})
|
||||
}
|
||||
},
|
||||
BYTE("byte") {
|
||||
override fun parse(element: JsonElement): NbtMatcher? {
|
||||
return parseGenericNumber(element,
|
||||
{ it.asByte },
|
||||
{ (it as? NbtByte)?.byteValue() },
|
||||
{ a, b ->
|
||||
if (a == b) Comparison.EQUAL
|
||||
else if (a < b) Comparison.LESS_THAN
|
||||
else Comparison.GREATER
|
||||
})
|
||||
}
|
||||
},
|
||||
;
|
||||
|
||||
abstract fun parse(element: JsonElement): NbtMatcher?
|
||||
}
|
||||
|
||||
enum class Comparison {
|
||||
LESS_THAN, EQUAL, GREATER
|
||||
}
|
||||
|
||||
inline fun <T : Any> parseGenericNumber(
|
||||
jsonElement: JsonElement,
|
||||
primitiveExtractor: (JsonPrimitive) -> T?,
|
||||
crossinline nbtExtractor: (NbtElement) -> T?,
|
||||
crossinline compare: (T, T) -> Comparison
|
||||
): NbtMatcher? {
|
||||
if (jsonElement is JsonPrimitive) {
|
||||
val expected = primitiveExtractor(jsonElement) ?: return null
|
||||
return NbtMatcher {
|
||||
val actual = nbtExtractor(it) ?: return@NbtMatcher false
|
||||
compare(actual, expected) == Comparison.EQUAL
|
||||
}
|
||||
}
|
||||
if (jsonElement is JsonObject) {
|
||||
val minElement = jsonElement.getAsJsonPrimitive("min")
|
||||
val min = if (minElement != null) primitiveExtractor(minElement) ?: return null else null
|
||||
val minExclusive = jsonElement.get("minExclusive")?.asBoolean ?: false
|
||||
val maxElement = jsonElement.getAsJsonPrimitive("max")
|
||||
val max = if (maxElement != null) primitiveExtractor(maxElement) ?: return null else null
|
||||
val maxExclusive = jsonElement.get("maxExclusive")?.asBoolean ?: true
|
||||
if (min == null && max == null) return null
|
||||
return NbtMatcher {
|
||||
val actual = nbtExtractor(it) ?: return@NbtMatcher false
|
||||
if (max != null) {
|
||||
val comp = compare(actual, max)
|
||||
if (comp == Comparison.GREATER) return@NbtMatcher false
|
||||
if (comp == Comparison.EQUAL && maxExclusive) return@NbtMatcher false
|
||||
}
|
||||
if (min != null) {
|
||||
val comp = compare(actual, min)
|
||||
if (comp == Comparison.LESS_THAN) return@NbtMatcher false
|
||||
if (comp == Comparison.EQUAL && minExclusive) return@NbtMatcher false
|
||||
}
|
||||
return@NbtMatcher true
|
||||
}
|
||||
}
|
||||
return null
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class MatchNumberExact(val number: Long) : NbtMatcher {
|
||||
override fun matches(nbt: NbtElement): Boolean {
|
||||
return when (nbt) {
|
||||
is NbtByte -> nbt.byteValue().toLong() == number
|
||||
is NbtInt -> nbt.intValue().toLong() == number
|
||||
is NbtShort -> nbt.shortValue().toLong() == number
|
||||
is NbtLong -> nbt.longValue().toLong() == number
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MatchStringExact(val string: String) : NbtMatcher {
|
||||
override fun matches(nbt: NbtElement): Boolean {
|
||||
return nbt is NbtString && nbt.asString() == string
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "MatchNbtStringExactly($string)"
|
||||
}
|
||||
}
|
||||
|
||||
class MatchString(val string: StringMatcher) : NbtMatcher {
|
||||
override fun matches(nbt: NbtElement): Boolean {
|
||||
return nbt is NbtString && string.matches(nbt.asString())
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "MatchNbtString($string)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ExtraAttributesPredicate(
|
||||
val path: NbtPrism,
|
||||
val matcher: NbtMatcher,
|
||||
) : FirmamentModelPredicate {
|
||||
|
||||
object Parser : FirmamentModelPredicateParser {
|
||||
override fun parse(jsonElement: JsonElement): FirmamentModelPredicate? {
|
||||
if (jsonElement !is JsonObject) return null
|
||||
val path = jsonElement.get("path") ?: return null
|
||||
val pathSegments = if (path is JsonArray) {
|
||||
path.map { (it as JsonPrimitive).asString }
|
||||
} else if (path is JsonPrimitive && path.isString) {
|
||||
path.asString.split(".")
|
||||
} else return null
|
||||
val matcher = NbtMatcher.Parser.parse(jsonElement.get("match") ?: jsonElement)
|
||||
?: return null
|
||||
return ExtraAttributesPredicate(NbtPrism(pathSegments), matcher)
|
||||
}
|
||||
}
|
||||
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
return path.access(stack.extraAttributes)
|
||||
.any { matcher.matches(it) }
|
||||
}
|
||||
}
|
||||
|
||||
class NbtPrism(val path: List<String>) {
|
||||
override fun toString(): String {
|
||||
return "Prism($path)"
|
||||
}
|
||||
fun access(root: NbtElement): Collection<NbtElement> {
|
||||
var rootSet = mutableListOf(root)
|
||||
var switch = mutableListOf<NbtElement>()
|
||||
for (pathSegment in path) {
|
||||
if (pathSegment == ".") continue
|
||||
for (element in rootSet) {
|
||||
if (element is NbtList) {
|
||||
if (pathSegment == "*")
|
||||
switch.addAll(element)
|
||||
val index = pathSegment.toIntOrNull() ?: continue
|
||||
if (index !in element.indices) continue
|
||||
switch.add(element[index])
|
||||
}
|
||||
if (element is NbtCompound) {
|
||||
if (pathSegment == "*")
|
||||
element.keys.mapTo(switch) { element.get(it)!! }
|
||||
switch.add(element.get(pathSegment) ?: continue)
|
||||
}
|
||||
}
|
||||
val temp = switch
|
||||
switch = rootSet
|
||||
rootSet = temp
|
||||
switch.clear()
|
||||
}
|
||||
return rootSet
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack.predicates
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonPrimitive
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicateParser
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.util.MC
|
||||
|
||||
class ItemPredicate(
|
||||
val item: Item
|
||||
) : FirmamentModelPredicate {
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
return stack.item == item
|
||||
}
|
||||
|
||||
object Parser : FirmamentModelPredicateParser {
|
||||
override fun parse(jsonElement: JsonElement): ItemPredicate? {
|
||||
if (jsonElement is JsonPrimitive && jsonElement.isString) {
|
||||
val itemKey = RegistryKey.of(RegistryKeys.ITEM,
|
||||
Identifier.tryParse(jsonElement.asString)
|
||||
?: return null)
|
||||
return ItemPredicate(MC.defaultItems.getOptional(itemKey).getOrNull()?.value() ?: return null)
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack.predicates
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicateParser
|
||||
import moe.nea.firmament.features.texturepack.StringMatcher
|
||||
import net.minecraft.item.ItemStack
|
||||
import moe.nea.firmament.util.mc.loreAccordingToNbt
|
||||
|
||||
class LorePredicate(val matcher: StringMatcher) : FirmamentModelPredicate {
|
||||
object Parser : FirmamentModelPredicateParser {
|
||||
override fun parse(jsonElement: JsonElement): FirmamentModelPredicate {
|
||||
return LorePredicate(StringMatcher.parse(jsonElement))
|
||||
}
|
||||
}
|
||||
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
val lore = stack.loreAccordingToNbt
|
||||
return lore.any { matcher.matches(it) }
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack.predicates
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import moe.nea.firmament.features.texturepack.CustomModelOverrideParser
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicateParser
|
||||
import net.minecraft.item.ItemStack
|
||||
|
||||
class NotPredicate(val children: Array<FirmamentModelPredicate>) : FirmamentModelPredicate {
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
return children.none { it.test(stack) }
|
||||
}
|
||||
|
||||
object Parser : FirmamentModelPredicateParser {
|
||||
override fun parse(jsonElement: JsonElement): FirmamentModelPredicate {
|
||||
return NotPredicate(CustomModelOverrideParser.parsePredicates(jsonElement as JsonObject).toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package moe.nea.firmament.features.texturepack.predicates
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonPrimitive
|
||||
import moe.nea.firmament.util.useMatch
|
||||
|
||||
abstract class NumberMatcher {
|
||||
abstract fun test(number: Number): Boolean
|
||||
|
||||
|
||||
companion object {
|
||||
fun parse(jsonElement: JsonElement): NumberMatcher? {
|
||||
if (jsonElement is JsonPrimitive) {
|
||||
if (jsonElement.isString) {
|
||||
val string = jsonElement.asString
|
||||
return parseRange(string) ?: parseOperator(string)
|
||||
}
|
||||
if (jsonElement.isNumber) {
|
||||
val number = jsonElement.asNumber
|
||||
val hasDecimals = (number.toString().contains("."))
|
||||
return MatchNumberExact(if (hasDecimals) number.toLong() else number.toDouble())
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private val intervalSpec =
|
||||
"(?<beginningOpen>[\\[\\(])(?<beginning>[0-9.]+)?,(?<ending>[0-9.]+)?(?<endingOpen>[\\]\\)])"
|
||||
.toPattern()
|
||||
|
||||
fun parseRange(string: String): RangeMatcher? {
|
||||
intervalSpec.useMatch<Nothing>(string) {
|
||||
// Open in the set-theory sense, meaning does not include its end.
|
||||
val beginningOpen = group("beginningOpen") == "("
|
||||
val endingOpen = group("endingOpen") == ")"
|
||||
val beginning = group("beginning")?.toDouble()
|
||||
val ending = group("ending")?.toDouble()
|
||||
return RangeMatcher(beginning, !beginningOpen, ending, !endingOpen)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
enum class Operator(val operator: String) {
|
||||
LESS("<") {
|
||||
override fun matches(comparisonResult: Int): Boolean {
|
||||
return comparisonResult < 0
|
||||
}
|
||||
},
|
||||
LESS_EQUALS("<=") {
|
||||
override fun matches(comparisonResult: Int): Boolean {
|
||||
return comparisonResult <= 0
|
||||
}
|
||||
},
|
||||
GREATER(">") {
|
||||
override fun matches(comparisonResult: Int): Boolean {
|
||||
return comparisonResult > 0
|
||||
}
|
||||
},
|
||||
GREATER_EQUALS(">=") {
|
||||
override fun matches(comparisonResult: Int): Boolean {
|
||||
return comparisonResult >= 0
|
||||
}
|
||||
},
|
||||
;
|
||||
|
||||
abstract fun matches(comparisonResult: Int): Boolean
|
||||
}
|
||||
|
||||
private val operatorPattern =
|
||||
"(?<operator>${Operator.entries.joinToString("|") { it.operator }})(?<value>[0-9.]+)".toPattern()
|
||||
|
||||
fun parseOperator(string: String): OperatorMatcher? {
|
||||
return operatorPattern.useMatch(string) {
|
||||
val operatorName = group("operator")
|
||||
val operator = Operator.entries.find { it.operator == operatorName }!!
|
||||
val value = group("value").toDouble()
|
||||
OperatorMatcher(operator, value)
|
||||
}
|
||||
}
|
||||
|
||||
data class OperatorMatcher(val operator: Operator, val value: Double) : NumberMatcher() {
|
||||
override fun test(number: Number): Boolean {
|
||||
return operator.matches(number.toDouble().compareTo(value))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data class MatchNumberExact(val number: Number) : NumberMatcher() {
|
||||
override fun test(number: Number): Boolean {
|
||||
return when (this.number) {
|
||||
is Double -> number.toDouble() == this.number.toDouble()
|
||||
else -> number.toLong() == this.number.toLong()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class RangeMatcher(
|
||||
val beginning: Double?,
|
||||
val beginningInclusive: Boolean,
|
||||
val ending: Double?,
|
||||
val endingInclusive: Boolean,
|
||||
) : NumberMatcher() {
|
||||
override fun test(number: Number): Boolean {
|
||||
val value = number.toDouble()
|
||||
if (beginning != null) {
|
||||
if (beginningInclusive) {
|
||||
if (value < beginning) return false
|
||||
} else {
|
||||
if (value <= beginning) return false
|
||||
}
|
||||
}
|
||||
if (ending != null) {
|
||||
if (endingInclusive) {
|
||||
if (value > ending) return false
|
||||
} else {
|
||||
if (value >= ending) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack.predicates
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import moe.nea.firmament.features.texturepack.CustomModelOverrideParser
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicateParser
|
||||
import net.minecraft.item.ItemStack
|
||||
|
||||
class OrPredicate(val children: Array<FirmamentModelPredicate>) : FirmamentModelPredicate {
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
return children.any { it.test(stack) }
|
||||
}
|
||||
|
||||
object Parser : FirmamentModelPredicateParser {
|
||||
override fun parse(jsonElement: JsonElement): FirmamentModelPredicate {
|
||||
val children =
|
||||
(jsonElement as JsonArray)
|
||||
.flatMap {
|
||||
CustomModelOverrideParser.parsePredicates(it as JsonObject)
|
||||
}
|
||||
.toTypedArray()
|
||||
return OrPredicate(children)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack.predicates
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicateParser
|
||||
import moe.nea.firmament.features.texturepack.RarityMatcher
|
||||
import moe.nea.firmament.features.texturepack.StringMatcher
|
||||
import net.minecraft.item.ItemStack
|
||||
import moe.nea.firmament.repo.ExpLadders
|
||||
import moe.nea.firmament.util.petData
|
||||
|
||||
data class PetPredicate(
|
||||
val petId: StringMatcher?,
|
||||
val tier: RarityMatcher?,
|
||||
val exp: NumberMatcher?,
|
||||
val candyUsed: NumberMatcher?,
|
||||
val level: NumberMatcher?,
|
||||
) : FirmamentModelPredicate {
|
||||
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
val petData = stack.petData ?: return false
|
||||
if (petId != null) {
|
||||
if (!petId.matches(petData.type)) return false
|
||||
}
|
||||
if (exp != null) {
|
||||
if (!exp.test(petData.exp)) return false
|
||||
}
|
||||
if (candyUsed != null) {
|
||||
if (!candyUsed.test(petData.candyUsed)) return false
|
||||
}
|
||||
if (tier != null) {
|
||||
if (!tier.match(petData.tier)) return false
|
||||
}
|
||||
val levelData by lazy(LazyThreadSafetyMode.NONE) {
|
||||
ExpLadders.getExpLadder(petData.type, petData.tier)
|
||||
.getPetLevel(petData.exp)
|
||||
}
|
||||
if (level != null) {
|
||||
if (!level.test(levelData.currentLevel)) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
object Parser : FirmamentModelPredicateParser {
|
||||
override fun parse(jsonElement: JsonElement): FirmamentModelPredicate? {
|
||||
if (jsonElement.isJsonPrimitive) {
|
||||
return PetPredicate(StringMatcher.Equals(jsonElement.asString, false), null, null, null, null)
|
||||
}
|
||||
if (jsonElement !is JsonObject) return null
|
||||
val idMatcher = jsonElement["id"]?.let(StringMatcher::parse)
|
||||
val expMatcher = jsonElement["exp"]?.let(NumberMatcher::parse)
|
||||
val levelMatcher = jsonElement["level"]?.let(NumberMatcher::parse)
|
||||
val candyMatcher = jsonElement["candyUsed"]?.let(NumberMatcher::parse)
|
||||
val tierMatcher = jsonElement["tier"]?.let(RarityMatcher::parse)
|
||||
return PetPredicate(
|
||||
idMatcher,
|
||||
tierMatcher,
|
||||
expMatcher,
|
||||
candyMatcher,
|
||||
levelMatcher,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.gui.screen.world.SelectWorldScreen
|
||||
import net.minecraft.component.type.MapIdComponent
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.boss.dragon.EnderDragonPart
|
||||
import net.minecraft.entity.damage.DamageSource
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.Fluid
|
||||
@@ -262,6 +263,10 @@ class FakeWorld(
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getEnderDragonParts(): MutableCollection<EnderDragonPart> {
|
||||
return mutableListOf()
|
||||
}
|
||||
|
||||
override fun getTickManager(): TickManager {
|
||||
return TickManager()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
package moe.nea.firmament.repo
|
||||
|
||||
import java.io.InputStream
|
||||
@@ -21,86 +20,86 @@ import net.minecraft.resource.ResourcePackInfo
|
||||
import net.minecraft.resource.ResourcePackSource
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.resource.metadata.ResourceMetadata
|
||||
import net.minecraft.resource.metadata.ResourceMetadataReader
|
||||
import net.minecraft.resource.metadata.ResourceMetadataSerializer
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.PathUtil
|
||||
import moe.nea.firmament.Firmament
|
||||
|
||||
class RepoModResourcePack(val basePath: Path) : ModResourcePack {
|
||||
companion object {
|
||||
fun append(packs: MutableList<in ModResourcePack>) {
|
||||
Firmament.logger.info("Registering mod resource pack")
|
||||
packs.add(RepoModResourcePack(RepoDownloadManager.repoSavedLocation))
|
||||
}
|
||||
companion object {
|
||||
fun append(packs: MutableList<in ModResourcePack>) {
|
||||
Firmament.logger.info("Registering mod resource pack")
|
||||
packs.add(RepoModResourcePack(RepoDownloadManager.repoSavedLocation))
|
||||
}
|
||||
|
||||
fun createResourceDirectly(identifier: Identifier): Optional<Resource> {
|
||||
val pack = RepoModResourcePack(RepoDownloadManager.repoSavedLocation)
|
||||
return Optional.of(
|
||||
Resource(
|
||||
pack,
|
||||
pack.open(ResourceType.CLIENT_RESOURCES, identifier) ?: return Optional.empty()
|
||||
) {
|
||||
val base =
|
||||
pack.open(ResourceType.CLIENT_RESOURCES, identifier.withPath(identifier.path + ".mcmeta"))
|
||||
if (base == null)
|
||||
ResourceMetadata.NONE
|
||||
else
|
||||
NamespaceResourceManager.loadMetadata(base)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
fun createResourceDirectly(identifier: Identifier): Optional<Resource> {
|
||||
val pack = RepoModResourcePack(RepoDownloadManager.repoSavedLocation)
|
||||
return Optional.of(
|
||||
Resource(
|
||||
pack,
|
||||
pack.open(ResourceType.CLIENT_RESOURCES, identifier) ?: return Optional.empty()
|
||||
) {
|
||||
val base =
|
||||
pack.open(ResourceType.CLIENT_RESOURCES, identifier.withPath(identifier.path + ".mcmeta"))
|
||||
if (base == null)
|
||||
ResourceMetadata.NONE
|
||||
else
|
||||
NamespaceResourceManager.loadMetadata(base)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
}
|
||||
override fun close() {
|
||||
}
|
||||
|
||||
override fun openRoot(vararg segments: String): InputSupplier<InputStream>? {
|
||||
return getFile(segments)?.let { InputSupplier.create(it) }
|
||||
}
|
||||
override fun openRoot(vararg segments: String): InputSupplier<InputStream>? {
|
||||
return getFile(segments)?.let { InputSupplier.create(it) }
|
||||
}
|
||||
|
||||
fun getFile(segments: Array<out String>): Path? {
|
||||
PathUtil.validatePath(*segments)
|
||||
val path = segments.fold(basePath, Path::resolve)
|
||||
if (!path.isRegularFile()) return null
|
||||
return path
|
||||
}
|
||||
fun getFile(segments: Array<out String>): Path? {
|
||||
PathUtil.validatePath(*segments)
|
||||
val path = segments.fold(basePath, Path::resolve)
|
||||
if (!path.isRegularFile()) return null
|
||||
return path
|
||||
}
|
||||
|
||||
override fun open(type: ResourceType?, id: Identifier): InputSupplier<InputStream>? {
|
||||
if (type != ResourceType.CLIENT_RESOURCES) return null
|
||||
if (id.namespace != "neurepo") return null
|
||||
val file = getFile(id.path.split("/").toTypedArray())
|
||||
return file?.let { InputSupplier.create(it) }
|
||||
}
|
||||
override fun open(type: ResourceType?, id: Identifier): InputSupplier<InputStream>? {
|
||||
if (type != ResourceType.CLIENT_RESOURCES) return null
|
||||
if (id.namespace != "neurepo") return null
|
||||
val file = getFile(id.path.split("/").toTypedArray())
|
||||
return file?.let { InputSupplier.create(it) }
|
||||
}
|
||||
|
||||
override fun findResources(
|
||||
type: ResourceType?,
|
||||
namespace: String,
|
||||
prefix: String,
|
||||
consumer: ResourcePack.ResultConsumer
|
||||
) {
|
||||
if (namespace != "neurepo") return
|
||||
if (type != ResourceType.CLIENT_RESOURCES) return
|
||||
override fun findResources(
|
||||
type: ResourceType?,
|
||||
namespace: String,
|
||||
prefix: String,
|
||||
consumer: ResourcePack.ResultConsumer
|
||||
) {
|
||||
if (namespace != "neurepo") return
|
||||
if (type != ResourceType.CLIENT_RESOURCES) return
|
||||
|
||||
val prefixPath = basePath.resolve(prefix)
|
||||
if (!prefixPath.exists())
|
||||
return
|
||||
Files.walk(prefixPath)
|
||||
.asSequence()
|
||||
.map { it.relativeTo(basePath) }
|
||||
.forEach {
|
||||
consumer.accept(Identifier.of("neurepo", it.toString()), InputSupplier.create(it))
|
||||
}
|
||||
}
|
||||
val prefixPath = basePath.resolve(prefix)
|
||||
if (!prefixPath.exists())
|
||||
return
|
||||
Files.walk(prefixPath)
|
||||
.asSequence()
|
||||
.map { it.relativeTo(basePath) }
|
||||
.forEach {
|
||||
consumer.accept(Identifier.of("neurepo", it.toString()), InputSupplier.create(it))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getNamespaces(type: ResourceType?): Set<String> {
|
||||
if (type != ResourceType.CLIENT_RESOURCES) return emptySet()
|
||||
return setOf("neurepo")
|
||||
}
|
||||
override fun getNamespaces(type: ResourceType?): Set<String> {
|
||||
if (type != ResourceType.CLIENT_RESOURCES) return emptySet()
|
||||
return setOf("neurepo")
|
||||
}
|
||||
|
||||
override fun <T> parseMetadata(metaReader: ResourceMetadataReader<T>): T? {
|
||||
return AbstractFileResourcePack.parseMetadata(
|
||||
metaReader, """
|
||||
override fun <T : Any?> parseMetadata(metadataSerializer: ResourceMetadataSerializer<T>?): T? {
|
||||
return AbstractFileResourcePack.parseMetadata(
|
||||
metadataSerializer, """
|
||||
{
|
||||
"pack": {
|
||||
"pack_format": 12,
|
||||
@@ -108,19 +107,20 @@ class RepoModResourcePack(val basePath: Path) : ModResourcePack {
|
||||
}
|
||||
}
|
||||
""".trimIndent().byteInputStream()
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun getInfo(): ResourcePackInfo {
|
||||
return ResourcePackInfo("neurepo", Text.literal("NEU Repo"), ResourcePackSource.BUILTIN, Optional.empty())
|
||||
}
|
||||
|
||||
override fun getFabricModMetadata(): ModMetadata {
|
||||
return FabricLoader.getInstance().getModContainer("firmament")
|
||||
.get().metadata
|
||||
}
|
||||
override fun getInfo(): ResourcePackInfo {
|
||||
return ResourcePackInfo("neurepo", Text.literal("NEU Repo"), ResourcePackSource.BUILTIN, Optional.empty())
|
||||
}
|
||||
|
||||
override fun createOverlay(overlay: String): ModResourcePack {
|
||||
return RepoModResourcePack(basePath.resolve(overlay))
|
||||
}
|
||||
override fun getFabricModMetadata(): ModMetadata {
|
||||
return FabricLoader.getInstance().getModContainer("firmament")
|
||||
.get().metadata
|
||||
}
|
||||
|
||||
override fun createOverlay(overlay: String): ModResourcePack {
|
||||
return RepoModResourcePack(basePath.resolve(overlay))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,6 @@ accessible class net/minecraft/client/render/RenderLayer$MultiPhaseParameters
|
||||
accessible class net/minecraft/client/font/TextRenderer$Drawer
|
||||
accessible field net/minecraft/client/gui/hud/InGameHud SCOREBOARD_ENTRY_COMPARATOR Ljava/util/Comparator;
|
||||
|
||||
accessible field net/minecraft/client/render/item/HeldItemRenderer itemRenderer Lnet/minecraft/client/render/item/ItemRenderer;
|
||||
accessible field net/minecraft/client/render/item/ItemModels missingModelSupplier Ljava/util/function/Supplier;
|
||||
mutable field net/minecraft/client/render/model/json/ModelOverrideList conditionTypes [Lnet/minecraft/util/Identifier;
|
||||
|
||||
accessible class net/minecraft/client/render/model/json/ModelOverride$Deserializer
|
||||
accessible class net/minecraft/client/render/model/json/ModelOverrideList$BakedOverride
|
||||
accessible field net/minecraft/entity/mob/CreeperEntity CHARGED Lnet/minecraft/entity/data/TrackedData;
|
||||
accessible method net/minecraft/entity/decoration/ArmorStandEntity setSmall (Z)V
|
||||
accessible field net/minecraft/entity/passive/AbstractHorseEntity items Lnet/minecraft/inventory/SimpleInventory;
|
||||
@@ -23,16 +17,3 @@ mutable field net/minecraft/screen/slot/Slot y I
|
||||
|
||||
accessible field net/minecraft/entity/player/PlayerEntity PLAYER_MODEL_PARTS Lnet/minecraft/entity/data/TrackedData;
|
||||
accessible field net/minecraft/client/render/WorldRenderer chunks Lnet/minecraft/client/render/BuiltChunkStorage;
|
||||
|
||||
# Fix package-private access methods
|
||||
accessible method net/minecraft/registry/entry/RegistryEntry$Reference setRegistryKey (Lnet/minecraft/registry/RegistryKey;)V
|
||||
accessible method net/minecraft/entity/LivingEntity getHitbox ()Lnet/minecraft/util/math/Box;
|
||||
accessible method net/minecraft/registry/entry/RegistryEntryList$Named <init> (Lnet/minecraft/registry/entry/RegistryEntryOwner;Lnet/minecraft/registry/tag/TagKey;)V
|
||||
accessible method net/minecraft/registry/entry/RegistryEntry$Reference setValue (Ljava/lang/Object;)V
|
||||
accessible field net/minecraft/client/render/model/WrapperBakedModel wrapped Lnet/minecraft/client/render/model/BakedModel;
|
||||
accessible method net/minecraft/entity/passive/TameableEntity isInSameTeam (Lnet/minecraft/entity/Entity;)Z
|
||||
accessible method net/minecraft/entity/Entity isInSameTeam (Lnet/minecraft/entity/Entity;)Z
|
||||
accessible method net/minecraft/registry/entry/RegistryEntry$Reference setTags (Ljava/util/Collection;)V
|
||||
accessible method net/minecraft/registry/entry/RegistryEntryList$Named setEntries (Ljava/util/List;)V
|
||||
accessible method net/minecraft/world/biome/source/util/VanillaBiomeParameters writeOverworldBiomeParameters (Ljava/util/function/Consumer;)V
|
||||
accessible method net/minecraft/world/gen/densityfunction/DensityFunctions createSurfaceNoiseRouter (Lnet/minecraft/registry/RegistryEntryLookup;Lnet/minecraft/registry/RegistryEntryLookup;ZZ)Lnet/minecraft/world/gen/noise/NoiseRouter;
|
||||
|
||||
Reference in New Issue
Block a user