From b12b57df29db973ddb35da34e16557a5f317a2a1 Mon Sep 17 00:00:00 2001 From: IThundxr Date: Tue, 14 Jan 2025 18:09:12 -0500 Subject: [PATCH] [1.21.4] Fix IClientItemExtensions#renderHelmetOverlay never being called (#1837) --- .../net/minecraft/client/gui/Gui.java.patch | 15 ++++++++ .../common/IClientItemExtensions.java | 24 ++++++++----- .../neoforge/debug/client/ClientTests.java | 36 +++++++++++++++++++ 3 files changed, 67 insertions(+), 8 deletions(-) diff --git a/patches/net/minecraft/client/gui/Gui.java.patch b/patches/net/minecraft/client/gui/Gui.java.patch index f48725af15..e573d66866 100644 --- a/patches/net/minecraft/client/gui/Gui.java.patch +++ b/patches/net/minecraft/client/gui/Gui.java.patch @@ -101,6 +101,21 @@ } private void renderCameraOverlays(GuiGraphics p_316735_, DeltaTracker p_348538_) { +@@ -236,8 +_,12 @@ + for (EquipmentSlot equipmentslot : EquipmentSlot.values()) { + ItemStack itemstack = this.minecraft.player.getItemBySlot(equipmentslot); + Equippable equippable = itemstack.get(DataComponents.EQUIPPABLE); +- if (equippable != null && equippable.slot() == equipmentslot && equippable.cameraOverlay().isPresent()) { +- this.renderTextureOverlay(p_316735_, equippable.cameraOverlay().get().withPath(p_380782_ -> "textures/" + p_380782_ + ".png"), 1.0F); ++ if (equippable != null && equippable.slot() == equipmentslot) { ++ if (equippable.cameraOverlay().isPresent()) { ++ this.renderTextureOverlay(p_316735_, equippable.cameraOverlay().get().withPath(p_380782_ -> "textures/" + p_380782_ + ".png"), 1.0F); ++ } ++ ++ net.neoforged.neoforge.client.extensions.common.IClientItemExtensions.of(itemstack).renderFirstPersonOverlay(itemstack, equipmentslot, this.minecraft.player, p_316735_, p_348538_); + } + } + } @@ -289,8 +_,12 @@ } diff --git a/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientItemExtensions.java b/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientItemExtensions.java index 4ba1dfb5b9..5533e4223c 100644 --- a/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientItemExtensions.java +++ b/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientItemExtensions.java @@ -6,7 +6,9 @@ package net.neoforged.neoforge.client.extensions.common; import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.DeltaTracker; import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.model.HumanoidModel; import net.minecraft.client.model.Model; import net.minecraft.client.player.LocalPlayer; @@ -23,6 +25,7 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.component.DyedItemColor; +import net.minecraft.world.item.equipment.Equippable; import net.neoforged.fml.LogicalSide; import net.neoforged.neoforge.client.ClientHooks; import net.neoforged.neoforge.client.IArmPoseTransformer; @@ -142,17 +145,22 @@ default Model getGenericArmorModel(ItemStack itemStack, EquipmentClientInfo.Laye default void setupModelAnimations(LivingEntity livingEntity, ItemStack itemStack, EquipmentSlot equipmentSlot, Model model, float limbSwing, float limbSwingAmount, float partialTick, float ageInTicks, float netHeadYaw, float headPitch) {} /** - * Called when the client starts rendering the HUD, and is wearing this item in the helmet slot. + * Called to render an overlay on the first-person camera. *

- * This is where pumpkins would render their overlay. + * This method will always be called to render an overlay, regardless of whether the client camera overlay provided + * by the {@link DataComponents#EQUIPPABLE Equippable data component} is present. If the equippable overlay is present + * (e.g. the pumpkin overlay), this method will be called after it has been rendered. + *

+ * This method should be used if the overlay is dynamic or has dynamic components. + * For a static overlay, prefer using {@link Equippable#cameraOverlay()}. * - * @param stack The item stack - * @param player The player entity - * @param width The viewport width - * @param height Viewport height - * @param partialTick Partial tick time, useful for interpolation + * @param stack The item stack that the player is wearing + * @param equipmentSlot The slot in which the player is wearing or holding the above item stack + * @param player The player entity + * @param guiGraphics The gui graphics + * @param deltaTracker The delta tracker */ - default void renderHelmetOverlay(ItemStack stack, Player player, int width, int height, float partialTick) {} + default void renderFirstPersonOverlay(ItemStack stack, EquipmentSlot equipmentSlot, Player player, GuiGraphics guiGraphics, DeltaTracker deltaTracker) {} /** * {@return Whether the item should bob when rendered in the world as an entity} diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientTests.java index b89c9e5394..63cc542c93 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientTests.java @@ -9,8 +9,11 @@ import java.nio.ByteBuffer; import java.util.concurrent.CompletableFuture; import javax.sound.sampled.AudioFormat; +import net.minecraft.client.DeltaTracker; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.client.resources.sounds.AbstractSoundInstance; @@ -21,7 +24,10 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.phys.Vec3; import net.neoforged.api.distmarker.Dist; @@ -29,7 +35,10 @@ import net.neoforged.neoforge.client.event.ClientTickEvent; import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent; import net.neoforged.neoforge.client.event.TextureAtlasStitchedEvent; +import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions; +import net.neoforged.neoforge.client.extensions.common.RegisterClientExtensionsEvent; import net.neoforged.neoforge.common.data.LanguageProvider; +import net.neoforged.neoforge.event.entity.player.PlayerEvent; import net.neoforged.testframework.DynamicTest; import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; @@ -107,6 +116,33 @@ static void namespacedDirectoryListerTest(final DynamicTest test) { }); } + @TestHolder(description = "Tests that helmets with custom rendering work", enabledByDefault = true) + static void customHelmetRendering(final DynamicTest test) { + var item = test.registrationHelper().items().registerItem("neo_helmet", properties -> new Item(properties.equippable(EquipmentSlot.HEAD))); + test.framework().modEventBus().addListener((final RegisterClientExtensionsEvent event) -> { + event.registerItem(new IClientItemExtensions() { + @Override + public void renderFirstPersonOverlay(ItemStack stack, EquipmentSlot equipmentSlot, Player player, GuiGraphics guiGraphics, DeltaTracker deltaTracker) { + guiGraphics.blit( + RenderType::guiTexturedOverlay, + ResourceLocation.withDefaultNamespace("textures/block/stone.png"), + 0, + 0, + 0, + 0, + guiGraphics.guiWidth(), + guiGraphics.guiHeight(), + guiGraphics.guiWidth(), + guiGraphics.guiHeight(), + -1); + } + }, item); + }); + test.eventListeners().forge().addListener((final PlayerEvent.PlayerLoggedInEvent event) -> { + test.requestConfirmation(event.getEntity(), Component.literal("Does stone cover the screen when wearing the *_custom_helmet_rendering:neo_helmet?")); + }); + } + private static final class SineSound extends AbstractSoundInstance { SineSound(Vec3 position) { super(ResourceLocation.fromNamespaceAndPath("neotests_audio_stream_test", "sine_wave"), SoundSource.BLOCKS, SoundInstance.createUnseededRandom());