From 5f095d7543eb314cd9ce96f4efef4a4f69e6bdd6 Mon Sep 17 00:00:00 2001
From: ChampionAsh5357
Date: Mon, 3 Feb 2025 14:07:40 -0500
Subject: [PATCH 1/5] fix(neoforge): Reimplement
EntityRenderersEvent.CreateSkullModels
---
.../blockentity/SkullBlockRenderer.java.patch | 16 +++++-
.../neoforge/client/ClientHooks.java | 19 ++++++-
.../client/event/EntityRenderersEvent.java | 57 ++++++++++++-------
.../oldtest/block/CustomHeadTest.java | 12 +++-
.../custom_head_test/items/blaze_head.json | 10 ++++
5 files changed, 88 insertions(+), 26 deletions(-)
create mode 100644 tests/src/main/resources/assets/custom_head_test/items/blaze_head.json
diff --git a/patches/net/minecraft/client/renderer/blockentity/SkullBlockRenderer.java.patch b/patches/net/minecraft/client/renderer/blockentity/SkullBlockRenderer.java.patch
index 2d8d0090603..6e9c950aae6 100644
--- a/patches/net/minecraft/client/renderer/blockentity/SkullBlockRenderer.java.patch
+++ b/patches/net/minecraft/client/renderer/blockentity/SkullBlockRenderer.java.patch
@@ -1,9 +1,19 @@
--- a/net/minecraft/client/renderer/blockentity/SkullBlockRenderer.java
+++ b/net/minecraft/client/renderer/blockentity/SkullBlockRenderer.java
-@@ -115,4 +_,14 @@
+@@ -57,7 +_,7 @@
+ case PIGLIN -> new PiglinHeadModel(p_387840_.bakeLayer(ModelLayers.PIGLIN_HEAD));
+ });
+ } else {
+- return null;
++ return net.neoforged.neoforge.client.ClientHooks.getModdedSkullModel(p_387840_, p_388801_); // Neo: Lookup model for modded skull types
+ }
+ }
+
+@@ -114,5 +_,15 @@
+ p_389624_ != null ? p_389624_ : Minecraft.getInstance().getSkinManager().getInsecureSkin(p_389483_.gameProfile()).texture()
)
: RenderType.entityCutoutNoCullZOffset(p_389624_ != null ? p_389624_ : SKIN_BY_TYPE.get(p_389566_));
- }
++ }
+
+ @Override
+ public net.minecraft.world.phys.AABB getRenderBoundingBox(SkullBlockEntity blockEntity) {
@@ -13,5 +23,5 @@
+ return new net.minecraft.world.phys.AABB(pos.getX() - .75, pos.getY() - .35, pos.getZ() - .75, pos.getX() + 1.75, pos.getY() + 1.0, pos.getZ() + 1.75);
+ }
+ return BlockEntityRenderer.super.getRenderBoundingBox(blockEntity);
-+ }
+ }
}
diff --git a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java
index 40d8b2c25e4..46e76692e4b 100644
--- a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java
+++ b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java
@@ -53,6 +53,8 @@
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipPositioner;
import net.minecraft.client.model.HumanoidModel;
+import net.minecraft.client.model.SkullModelBase;
+import net.minecraft.client.model.geom.EntityModelSet;
import net.minecraft.client.model.geom.ModelLayerLocation;
import net.minecraft.client.model.geom.builders.LayerDefinition;
import net.minecraft.client.multiplayer.ClientLevel;
@@ -124,6 +126,7 @@
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.SkullBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
@@ -685,7 +688,7 @@ public static boolean shouldRenderEffect(MobEffectInstance effectInstance) {
return IClientMobEffectExtensions.of(effectInstance).isVisibleInInventory(effectInstance);
}
- private static final Map> layerDefinitions = new HashMap<>();
+ private static Map> layerDefinitions = new HashMap<>();
public static void registerLayerDefinition(ModelLayerLocation layerLocation, Supplier supplier) {
layerDefinitions.put(layerLocation, supplier);
@@ -695,6 +698,19 @@ public static void loadLayerDefinitions(ImmutableMap.Builder builder.put(k, v.get()));
}
+ private static Map> skullModelsByType = null;
+
+ @Nullable
+ public static SkullModelBase getModdedSkullModel(EntityModelSet modelSet, SkullBlock.Type type) {
+ return skullModelsByType.getOrDefault(type, set -> null).apply(modelSet);
+ }
+
+ public static void registerModdedSkullModels() {
+ ImmutableMap.Builder> builder = ImmutableMap.builder();
+ ModLoader.postEvent(new EntityRenderersEvent.CreateSkullModels(builder));
+ skullModelsByType = builder.build();
+ }
+
private static final ResourceLocation ICON_SHEET = ResourceLocation.fromNamespaceAndPath(NeoForgeVersion.MOD_ID, "textures/gui/icons.png");
public static void firePlayerLogin(MultiPlayerGameMode pc, LocalPlayer player, Connection networkManager) {
@@ -982,6 +998,7 @@ public static void initClientHooks(Minecraft mc, ReloadableResourceManager resou
MenuScreens.init();
ModLoader.postEvent(new RegisterClientReloadListenersEvent(resourceManager));
ModLoader.postEvent(new EntityRenderersEvent.RegisterLayerDefinitions());
+ registerModdedSkullModels();
ModLoader.postEvent(new EntityRenderersEvent.RegisterRenderers());
ModLoader.postEvent(new RegisterRenderStateModifiersEvent());
ClientTooltipComponentManager.init();
diff --git a/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java b/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java
index fc2d26c7ac9..8ce064982f3 100644
--- a/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java
+++ b/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java
@@ -8,12 +8,14 @@
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import java.util.Set;
+import java.util.function.Function;
import java.util.function.Supplier;
import net.minecraft.client.model.SkullModel;
import net.minecraft.client.model.SkullModelBase;
import net.minecraft.client.model.geom.EntityModelSet;
import net.minecraft.client.model.geom.ModelLayerLocation;
import net.minecraft.client.model.geom.ModelLayers;
+import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.client.model.geom.builders.LayerDefinition;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
@@ -27,7 +29,6 @@
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.SkullBlock;
-import net.minecraft.world.level.block.SkullBlock.Type;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.neoforged.bus.api.Event;
@@ -195,43 +196,59 @@ public EntityRendererProvider.Context getContext() {
}
/**
- * Fired for registering additional {@linkplain net.minecraft.client.model.SkullModelBase skull models} at the appropriate time.
+ * Fired for registering additional {@linkplain net.minecraft.client.model.SkullModelBase skull models}.
*
- * This event is not {@linkplain ICancellableEvent cancellable}, and does not {@linkplain HasResult have a result}.
+ * This event is not {@linkplain ICancellableEvent cancellable}, and does not have a result.
*
* This event is fired on the mod-specific event bus,
* only on the {@linkplain LogicalSide#CLIENT logical client}.
*/
public static class CreateSkullModels extends EntityRenderersEvent {
- private final ImmutableMap.Builder builder;
- private final EntityModelSet entityModelSet;
+ private final ImmutableMap.Builder> builder;
@ApiStatus.Internal
- public CreateSkullModels(ImmutableMap.Builder builder, EntityModelSet entityModelSet) {
+ public CreateSkullModels(ImmutableMap.Builder> builder) {
this.builder = builder;
- this.entityModelSet = entityModelSet;
}
/**
- * {@return the set of entity models}
+ * Registers a {@link SkullModel} for a skull block with the given {@link SkullBlock.Type}.
+ *
+ * @param type a unique skull type; an exception will be thrown later if multiple mods (including vanilla)
+ * register models for the same type
+ * @param layerLocation the key that identifies the {@link LayerDefinition} used by the model
+ */
+ public void registerSkullModel(SkullBlock.Type type, ModelLayerLocation layerLocation) {
+ this.registerSkullModel(type, layerLocation, SkullModel::new);
+ }
+
+ /**
+ * Registers the entity model for a skull block with the given {@link SkullBlock.Type}.
+ *
+ * @param type a unique skull type; an exception will be thrown later if multiple mods (including vanilla)
+ * register models for the same type
+ * @param layerLocation the key that identifies the {@link LayerDefinition} used by the model
+ * @param factory the factory to create the skull model instance, taking in the root {@link ModelPart} and
+ * returning the model.
*/
- public EntityModelSet getEntityModelSet() {
- return entityModelSet;
+ public void registerSkullModel(SkullBlock.Type type, ModelLayerLocation layerLocation, Function factory) {
+ this.registerSkullModel(type, modelSet -> factory.apply(modelSet.bakeLayer(layerLocation)));
}
/**
- * Registers the constructor for a skull block with the given {@link SkullBlock.Type}.
- * These will be inserted into the maps used by the item, entity, and block model renderers at the appropriate
- * time.
+ * Registers the entity model for a skull block with the given {@link SkullBlock.Type}.
*
- * @param type a unique skull type; an exception will be thrown later if multiple mods (including vanilla)
- * register models for the same type
- * @param model the skull model instance. A typical implementation will simply bake a model using
- * {@link EntityModelSet#bakeLayer(ModelLayerLocation)} and pass it to the constructor for
- * {@link SkullModel}.
+ * @param type a unique skull type; an exception will be thrown later if multiple mods (including vanilla)
+ * register models for the same type
+ * @param factory the factory to create the skull model instance. A typical implementation will simply bake
+ * a model using {@link EntityModelSet#bakeLayer(ModelLayerLocation)} and pass it to the
+ * constructor for {@link SkullModel}
*/
- public void registerSkullModel(SkullBlock.Type type, SkullModelBase model) {
- builder.put(type, model);
+ public void registerSkullModel(SkullBlock.Type type, Function factory) {
+ if (type instanceof SkullBlock.Types) {
+ throw new IllegalArgumentException("Cannot register skull model for vanilla skull type: " + type.getSerializedName());
+ }
+ builder.put(type, factory);
}
}
}
diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/block/CustomHeadTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/block/CustomHeadTest.java
index 7c4f41299c4..c6ae6c79df7 100644
--- a/tests/src/main/java/net/neoforged/neoforge/oldtest/block/CustomHeadTest.java
+++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/block/CustomHeadTest.java
@@ -8,10 +8,12 @@
import net.minecraft.client.model.SkullModel;
import net.minecraft.client.model.geom.ModelLayerLocation;
import net.minecraft.client.renderer.blockentity.SkullBlockRenderer;
+import net.minecraft.client.renderer.special.SkullSpecialRenderer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Rarity;
@@ -32,6 +34,7 @@
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
import net.neoforged.neoforge.client.event.EntityRenderersEvent;
+import net.neoforged.neoforge.client.event.RegisterSpecialBlockModelRendererEvent;
import net.neoforged.neoforge.common.util.Lazy;
import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent;
import net.neoforged.neoforge.registries.DeferredBlock;
@@ -53,7 +56,7 @@ public class CustomHeadTest {
private static final DeferredRegister> BLOCK_ENTITIES = DeferredRegister.create(BuiltInRegistries.BLOCK_ENTITY_TYPE, MODID);
private static final DeferredBlock BLAZE_HEAD = BLOCKS.registerBlock("blaze_head", props -> new CustomSkullBlock(SkullType.BLAZE, props), BlockBehaviour.Properties.of().strength(1.0F));
private static final DeferredBlock BLAZE_HEAD_WALL = BLOCKS.registerBlock("blaze_wall_head", props -> new CustomWallSkullBlock(SkullType.BLAZE, props.strength(1.0F).overrideLootTable(BLAZE_HEAD.get().getLootTable())), BlockBehaviour.Properties.of());
- private static final DeferredItem- BLAZE_HEAD_ITEM = ITEMS.registerItem("blaze_head", props -> new StandingAndWallBlockItem(BLAZE_HEAD.get(), BLAZE_HEAD_WALL.get(), Direction.DOWN, props.rarity(Rarity.UNCOMMON)));
+ private static final DeferredItem
- BLAZE_HEAD_ITEM = ITEMS.registerItem("blaze_head", props -> new StandingAndWallBlockItem(BLAZE_HEAD.get(), BLAZE_HEAD_WALL.get(), Direction.DOWN, props.rarity(Rarity.UNCOMMON).equippableUnswappable(EquipmentSlot.HEAD)));
private static final DeferredHolder, BlockEntityType> CUSTOM_SKULL = BLOCK_ENTITIES.register("custom_skull", () -> new BlockEntityType<>(CustomSkullBlockEntity::new, BLAZE_HEAD.get(), BLAZE_HEAD_WALL.get()));
public CustomHeadTest(IEventBus modBus) {
@@ -135,7 +138,12 @@ static void clientSetupEvent(FMLClientSetupEvent event) {
@SubscribeEvent
static void registerSkullModel(EntityRenderersEvent.CreateSkullModels event) {
- event.registerSkullModel(SkullType.BLAZE, new SkullModel(event.getEntityModelSet().bakeLayer(ClientEvents.BLAZE_HEAD_LAYER)));
+ event.registerSkullModel(SkullType.BLAZE, ClientEvents.BLAZE_HEAD_LAYER);
+ }
+
+ @SubscribeEvent
+ static void registerSpecialBlockRenderer(RegisterSpecialBlockModelRendererEvent event) {
+ event.register(BLAZE_HEAD.get(), new SkullSpecialRenderer.Unbaked(SkullType.BLAZE));
}
}
}
diff --git a/tests/src/main/resources/assets/custom_head_test/items/blaze_head.json b/tests/src/main/resources/assets/custom_head_test/items/blaze_head.json
new file mode 100644
index 00000000000..2c01f4dbc75
--- /dev/null
+++ b/tests/src/main/resources/assets/custom_head_test/items/blaze_head.json
@@ -0,0 +1,10 @@
+{
+ "model": {
+ "type": "minecraft:special",
+ "base": "custom_head_test:item/blaze_head",
+ "model": {
+ "type": "minecraft:head",
+ "kind": "blaze"
+ }
+ }
+}
\ No newline at end of file
From 82d158cfbd809fd1696776732b110d855621523f Mon Sep 17 00:00:00 2001
From: ChampionAsh5357
Date: Mon, 3 Feb 2025 14:10:59 -0500
Subject: [PATCH 2/5] chore(neoforge): Make register method private
---
src/main/java/net/neoforged/neoforge/client/ClientHooks.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java
index 46e76692e4b..f7d59da318d 100644
--- a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java
+++ b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java
@@ -705,7 +705,7 @@ public static SkullModelBase getModdedSkullModel(EntityModelSet modelSet, SkullB
return skullModelsByType.getOrDefault(type, set -> null).apply(modelSet);
}
- public static void registerModdedSkullModels() {
+ private static void registerModdedSkullModels() {
ImmutableMap.Builder> builder = ImmutableMap.builder();
ModLoader.postEvent(new EntityRenderersEvent.CreateSkullModels(builder));
skullModelsByType = builder.build();
From 866c46b61a7f6932d218fe6f87a643d8591febe8 Mon Sep 17 00:00:00 2001
From: ChampionAsh5357
Date: Tue, 4 Feb 2025 14:23:44 -0500
Subject: [PATCH 3/5] fix(neoforge): Use map instead of builder and revert
layer def changes
---
.../net/neoforged/neoforge/client/ClientHooks.java | 12 +++---------
.../neoforge/client/event/EntityRenderersEvent.java | 11 ++++++-----
2 files changed, 9 insertions(+), 14 deletions(-)
diff --git a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java
index f7d59da318d..b19889061fe 100644
--- a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java
+++ b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java
@@ -688,7 +688,7 @@ public static boolean shouldRenderEffect(MobEffectInstance effectInstance) {
return IClientMobEffectExtensions.of(effectInstance).isVisibleInInventory(effectInstance);
}
- private static Map> layerDefinitions = new HashMap<>();
+ private static final Map> layerDefinitions = new HashMap<>();
public static void registerLayerDefinition(ModelLayerLocation layerLocation, Supplier supplier) {
layerDefinitions.put(layerLocation, supplier);
@@ -698,19 +698,13 @@ public static void loadLayerDefinitions(ImmutableMap.Builder builder.put(k, v.get()));
}
- private static Map> skullModelsByType = null;
+ private static final Map> skullModelsByType = new HashMap<>();
@Nullable
public static SkullModelBase getModdedSkullModel(EntityModelSet modelSet, SkullBlock.Type type) {
return skullModelsByType.getOrDefault(type, set -> null).apply(modelSet);
}
- private static void registerModdedSkullModels() {
- ImmutableMap.Builder> builder = ImmutableMap.builder();
- ModLoader.postEvent(new EntityRenderersEvent.CreateSkullModels(builder));
- skullModelsByType = builder.build();
- }
-
private static final ResourceLocation ICON_SHEET = ResourceLocation.fromNamespaceAndPath(NeoForgeVersion.MOD_ID, "textures/gui/icons.png");
public static void firePlayerLogin(MultiPlayerGameMode pc, LocalPlayer player, Connection networkManager) {
@@ -998,7 +992,7 @@ public static void initClientHooks(Minecraft mc, ReloadableResourceManager resou
MenuScreens.init();
ModLoader.postEvent(new RegisterClientReloadListenersEvent(resourceManager));
ModLoader.postEvent(new EntityRenderersEvent.RegisterLayerDefinitions());
- registerModdedSkullModels();
+ ModLoader.postEvent(new EntityRenderersEvent.CreateSkullModels(skullModelsByType));
ModLoader.postEvent(new EntityRenderersEvent.RegisterRenderers());
ModLoader.postEvent(new RegisterRenderStateModifiersEvent());
ClientTooltipComponentManager.init();
diff --git a/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java b/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java
index 8ce064982f3..096ffd0f308 100644
--- a/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java
+++ b/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java
@@ -5,7 +5,6 @@
package net.neoforged.neoforge.client.event;
-import com.google.common.collect.ImmutableMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
@@ -204,11 +203,11 @@ public EntityRendererProvider.Context getContext() {
* only on the {@linkplain LogicalSide#CLIENT logical client}.
*/
public static class CreateSkullModels extends EntityRenderersEvent {
- private final ImmutableMap.Builder> builder;
+ private final Map> skullModels;
@ApiStatus.Internal
- public CreateSkullModels(ImmutableMap.Builder> builder) {
- this.builder = builder;
+ public CreateSkullModels(Map> skullModels) {
+ this.skullModels = skullModels;
}
/**
@@ -248,7 +247,9 @@ public void registerSkullModel(SkullBlock.Type type, Function
Date: Tue, 4 Feb 2025 17:09:08 -0500
Subject: [PATCH 4/5] fix(neoforge): Add xfact's suggestion
Co-authored-by: Dennis C
---
.../neoforged/neoforge/client/event/EntityRenderersEvent.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java b/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java
index 096ffd0f308..c64181dda3e 100644
--- a/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java
+++ b/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java
@@ -237,8 +237,8 @@ public void registerSkullModel(SkullBlock.Type type, ModelLayerLocation layerLoc
/**
* Registers the entity model for a skull block with the given {@link SkullBlock.Type}.
*
- * @param type a unique skull type; an exception will be thrown later if multiple mods (including vanilla)
- * register models for the same type
+ * @param type a unique skull type; an exception will be thrown if multiple mods register models for
+ * the same type or a mod tries to register a model for a vanilla type
* @param factory the factory to create the skull model instance. A typical implementation will simply bake
* a model using {@link EntityModelSet#bakeLayer(ModelLayerLocation)} and pass it to the
* constructor for {@link SkullModel}
From 3fc6e30239f00c216dc2efb74d141699185c90a9 Mon Sep 17 00:00:00 2001
From: ChampionAsh5357
Date: Wed, 5 Feb 2025 16:26:26 -0500
Subject: [PATCH 5/5] fix(neoforge): Update the other two messages
Co-authored-by: Dennis C
---
.../neoforge/client/event/EntityRenderersEvent.java | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java b/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java
index c64181dda3e..d71fa9cf0d5 100644
--- a/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java
+++ b/src/main/java/net/neoforged/neoforge/client/event/EntityRenderersEvent.java
@@ -213,8 +213,8 @@ public CreateSkullModels(Map