From 3661620045bffe573ccef8d4f692b71a73f67202 Mon Sep 17 00:00:00 2001 From: Spectrall <105946758+Spectrall368@users.noreply.github.com> Date: Sat, 23 May 2026 23:57:49 +0200 Subject: [PATCH] Add ForgeSpawnEggItem to lazily handle EntityTypes (#10640) --- .../IDispenseItemBehavior.java.patch | 26 +++- .../minecraft/item/SpawnEggItem.java.patch | 26 ++++ .../common/ForgeSpawnEggItem.java | 134 ++++++++++++++++++ .../common/extensions/IForgeEntity.java | 3 +- src/main/resources/forge.sas | 1 + .../debug/item/ForgeSpawnEggItemTest.java | 79 +++++++++++ src/test/resources/META-INF/mods.toml | 2 + .../models/item/test_spawn_egg.json | 3 + 8 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 patches/minecraft/net/minecraft/item/SpawnEggItem.java.patch create mode 100644 src/main/java/net/minecraftforge/common/ForgeSpawnEggItem.java create mode 100644 src/test/java/net/minecraftforge/debug/item/ForgeSpawnEggItemTest.java create mode 100644 src/test/resources/assets/forge_spawnegg_test/models/item/test_spawn_egg.json diff --git a/patches/minecraft/net/minecraft/dispenser/IDispenseItemBehavior.java.patch b/patches/minecraft/net/minecraft/dispenser/IDispenseItemBehavior.java.patch index 0fa8480c8d..ef81931b91 100644 --- a/patches/minecraft/net/minecraft/dispenser/IDispenseItemBehavior.java.patch +++ b/patches/minecraft/net/minecraft/dispenser/IDispenseItemBehavior.java.patch @@ -1,6 +1,28 @@ --- a/net/minecraft/dispenser/IDispenseItemBehavior.java +++ b/net/minecraft/dispenser/IDispenseItemBehavior.java -@@ -270,8 +270,9 @@ +@@ -55,6 +55,7 @@ + import net.minecraft.world.World; + + public interface IDispenseItemBehavior { ++ org.apache.logging.log4j.Logger LOGGER = org.apache.logging.log4j.LogManager.getLogger(); + IDispenseItemBehavior field_223216_a_ = (p_210297_0_, p_210297_1_) -> { + return p_210297_1_; + }; +@@ -155,7 +156,12 @@ + public ItemStack func_82487_b(IBlockSource p_82487_1_, ItemStack p_82487_2_) { + Direction direction = p_82487_1_.func_189992_e().func_177229_b(DispenserBlock.field_176441_a); + EntityType entitytype = ((SpawnEggItem)p_82487_2_.func_77973_b()).func_208076_b(p_82487_2_.func_77978_p()); +- entitytype.func_220331_a(p_82487_1_.func_197524_h(), p_82487_2_, (PlayerEntity)null, p_82487_1_.func_180699_d().func_177972_a(direction), SpawnReason.DISPENSER, direction != Direction.UP, false); ++ try { // FORGE: fix potential crash ++ entitytype.func_220331_a(p_82487_1_.func_197524_h(), p_82487_2_, (PlayerEntity) null, p_82487_1_.func_180699_d().func_177972_a(direction), SpawnReason.DISPENSER, direction != Direction.UP, false); ++ } catch (Exception e) { ++ LOGGER.error("Error while dispensing spawn egg from dispenser at {}", p_82487_1_.func_180699_d(), e); ++ return ItemStack.field_190927_a; ++ } + p_82487_2_.func_190918_g(1); + return p_82487_2_; + } +@@ -270,8 +276,9 @@ world.func_175656_a(blockpos, Blocks.field_150480_ab.func_176223_P()); } else if (FlintAndSteelItem.func_219997_a(blockstate)) { world.func_175656_a(blockpos, blockstate.func_206870_a(BlockStateProperties.field_208190_q, Boolean.valueOf(true))); @@ -12,7 +34,7 @@ world.func_217377_a(blockpos, false); } else { this.field_218407_b = false; -@@ -370,15 +371,23 @@ +@@ -370,15 +377,23 @@ } DispenserBlock.func_199774_a(Items.field_151097_aZ.func_199767_j(), new OptionalDispenseBehavior() { diff --git a/patches/minecraft/net/minecraft/item/SpawnEggItem.java.patch b/patches/minecraft/net/minecraft/item/SpawnEggItem.java.patch new file mode 100644 index 0000000000..c68b3c47bf --- /dev/null +++ b/patches/minecraft/net/minecraft/item/SpawnEggItem.java.patch @@ -0,0 +1,26 @@ +--- a/net/minecraft/item/SpawnEggItem.java ++++ b/net/minecraft/item/SpawnEggItem.java +@@ -35,11 +35,14 @@ + private final int field_195989_d; + private final EntityType field_200890_d; + ++ /** @deprecated Forge: Use {@link net.minecraftforge.common.ForgeSpawnEggItem} instead for suppliers */ ++ @Deprecated + public SpawnEggItem(EntityType p_i48465_1_, int p_i48465_2_, int p_i48465_3_, Item.Properties p_i48465_4_) { + super(p_i48465_4_); + this.field_200890_d = p_i48465_1_; + this.field_195988_c = p_i48465_2_; + this.field_195989_d = p_i48465_3_; ++ if (p_i48465_1_ != null) + field_195987_b.put(p_i48465_1_, this); + } + +@@ -123,6 +126,8 @@ + return p_195983_1_ == 0 ? this.field_195988_c : this.field_195989_d; + } + ++ /** @deprecated Forge: call {@link net.minecraftforge.common.ForgeSpawnEggItem#fromEntityType(EntityType)} instead */ ++ @Deprecated + public static SpawnEggItem func_200889_b(@Nullable EntityType p_200889_0_) { + return field_195987_b.get(p_200889_0_); + } diff --git a/src/main/java/net/minecraftforge/common/ForgeSpawnEggItem.java b/src/main/java/net/minecraftforge/common/ForgeSpawnEggItem.java new file mode 100644 index 0000000000..ee0c8fef38 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/ForgeSpawnEggItem.java @@ -0,0 +1,134 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common; + +import net.minecraft.block.DispenserBlock; +import net.minecraft.dispenser.DefaultDispenseItemBehavior; +import net.minecraft.dispenser.IBlockSource; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.SpawnReason; +import net.minecraft.item.ItemStack; +import net.minecraft.item.SpawnEggItem; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.Direction; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.ColorHandlerEvent; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +public class ForgeSpawnEggItem extends SpawnEggItem +{ + private static final List MOD_EGGS = new ArrayList<>(); + private static final Map, ForgeSpawnEggItem> TYPE_MAP = new IdentityHashMap<>(); + private final Supplier> typeSupplier; + + public ForgeSpawnEggItem(Supplier> type, int backgroundColor, int highlightColor, Properties props) + { + super((EntityType) null, backgroundColor, highlightColor, props); + this.typeSupplier = type; + + MOD_EGGS.add(this); + } + + @Override + public EntityType getType(@Nullable CompoundNBT tag) + { + EntityType type = super.getType(tag); + return type != null ? type : typeSupplier.get(); + } + + @Nullable + protected DefaultDispenseItemBehavior createDispenseBehavior() + { + return DEFAULT_DISPENSE_BEHAVIOR; + } + + @Nullable + public static SpawnEggItem fromEntityType(@Nullable EntityType type) + { + SpawnEggItem ret = TYPE_MAP.get(type); + return ret != null ? ret : SpawnEggItem.getEgg(type); + } + + + + private static final DefaultDispenseItemBehavior DEFAULT_DISPENSE_BEHAVIOR = new DefaultDispenseItemBehavior() + { + protected ItemStack execute(IBlockSource source, ItemStack stack) + { + Direction face = source.getBlockState().get(DispenserBlock.FACING); + EntityType type = ((SpawnEggItem) stack.getItem()).getType(stack.getTag()); + + // FORGE: fix potential crash + try + { + type.spawn(source.getWorld(), stack, null, source.getBlockPos().offset(face), SpawnReason.DISPENSER, face != Direction.UP, false); + } + catch (Exception exception) + { + DefaultDispenseItemBehavior.LOGGER.error("Error while dispensing spawn egg from dispenser at {}", source.getBlockPos(), exception); + return ItemStack.EMPTY; + } + + stack.shrink(1); + return stack; + } + }; + + @Mod.EventBusSubscriber(modid = "forge", bus = Mod.EventBusSubscriber.Bus.MOD) + private static class CommonHandler + { + @SubscribeEvent + public static void onCommonSetup(FMLCommonSetupEvent event) + { + MOD_EGGS.forEach(egg -> + { + DefaultDispenseItemBehavior dispenseBehavior = egg.createDispenseBehavior(); + if (dispenseBehavior != null) + { + DispenserBlock.registerDispenseBehavior(egg, dispenseBehavior); + } + + TYPE_MAP.put(egg.typeSupplier.get(), egg); + }); + } + } + + @Mod.EventBusSubscriber(value = Dist.CLIENT, modid = "forge", bus = Mod.EventBusSubscriber.Bus.MOD) + private static class ColorRegisterHandler + { + @SubscribeEvent(priority = EventPriority.HIGHEST) + public static void registerSpawnEggColors(ColorHandlerEvent.Item event) + { + MOD_EGGS.forEach(egg -> + event.getItemColors().register((stack, layer) -> egg.getColor(layer), egg) + ); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgeEntity.java b/src/main/java/net/minecraftforge/common/extensions/IForgeEntity.java index 054e9a7e8d..03e4e1a490 100644 --- a/src/main/java/net/minecraftforge/common/extensions/IForgeEntity.java +++ b/src/main/java/net/minecraftforge/common/extensions/IForgeEntity.java @@ -39,6 +39,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; +import net.minecraftforge.common.ForgeSpawnEggItem; import net.minecraftforge.common.capabilities.ICapabilitySerializable; public interface IForgeEntity extends ICapabilitySerializable @@ -115,7 +116,7 @@ public interface IForgeEntity extends ICapabilitySerializable return new ItemStack(Items.END_CRYSTAL); else { - SpawnEggItem egg = SpawnEggItem.getEgg(getEntity().getType()); + SpawnEggItem egg = ForgeSpawnEggItem.fromEntityType(getEntity().getType()); if (egg != null) return new ItemStack(egg); } return ItemStack.EMPTY; diff --git a/src/main/resources/forge.sas b/src/main/resources/forge.sas index d7857e112c..5ea2ab71ae 100644 --- a/src/main/resources/forge.sas +++ b/src/main/resources/forge.sas @@ -34,6 +34,7 @@ net/minecraft/block/SoundType func_185846_f()Lnet/minecraft/util/SoundEvent; # g net/minecraft/item/DyeColor func_196057_c()I # getSwappedColorValue net/minecraft/item/DyeColor func_218388_g()I # getTextColor net/minecraft/item/DyeColor func_196058_b(I)Lnet/minecraft/item/DyeColor; # byFireworkColor +net/minecraft/item/SpawnEggItem func_200889_b(Lnet/minecraft/entity/EntityType;)Lnet/minecraft/item/SpawnEggItem; # byId net/minecraft/item/crafting/Ingredient func_193365_a()[Lnet/minecraft/item/ItemStack; # getMatchingStacks net/minecraft/item/crafting/Ingredient func_193369_a([Lnet/minecraft/item/ItemStack;)Lnet/minecraft/item/crafting/Ingredient; # fromStacks net/minecraft/item/crafting/IRecipe func_222128_h()Lnet/minecraft/item/ItemStack; # getIcon diff --git a/src/test/java/net/minecraftforge/debug/item/ForgeSpawnEggItemTest.java b/src/test/java/net/minecraftforge/debug/item/ForgeSpawnEggItemTest.java new file mode 100644 index 0000000000..5f1a92d704 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/item/ForgeSpawnEggItemTest.java @@ -0,0 +1,79 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.debug.item; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.entity.EntityRendererManager; +import net.minecraft.client.renderer.entity.PigRenderer; +import net.minecraft.entity.EntityClassification; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.passive.PigEntity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemGroup; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.common.ForgeSpawnEggItem; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.RegistryObject; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; + +@Mod(value = ForgeSpawnEggItemTest.MODID) +public class ForgeSpawnEggItemTest +{ + static final String MODID = "forge_spawnegg_test"; + static final boolean ENABLED = true; + + private static final DeferredRegister> ENTITIES = new DeferredRegister<>(ForgeRegistries.ENTITIES, MODID); + private static final RegistryObject> ENTITY = ENTITIES.register("test_entity", () -> + EntityType.Builder.create(PigEntity::new, EntityClassification.CREATURE).size(1, 1).build("test_entity") + ); + + private static final DeferredRegister ITEMS = new DeferredRegister<>(ForgeRegistries.ITEMS, MODID); + private static final RegistryObject EGG = ITEMS.register("test_spawn_egg", () -> + new ForgeSpawnEggItem(ENTITY, 0x0000FF, 0xFF0000, new Item.Properties().group(ItemGroup.MISC)) + ); + + public ForgeSpawnEggItemTest() + { + if (ENABLED) + { + ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus()); + ENTITIES.register(FMLJavaModLoadingContext.get().getModEventBus()); + + FMLJavaModLoadingContext.get().getModEventBus().register(this); + } + } + + @Mod.EventBusSubscriber(modid = MODID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) + private static class ClientEvents + { + @SubscribeEvent + public static void onClientSetup(final FMLClientSetupEvent event) + { + if (!ENABLED) { return; } + + EntityRendererManager manager = Minecraft.getInstance().getRenderManager(); + manager.register(PigEntity.class, new PigRenderer(manager)); + } + } +} \ No newline at end of file diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml index 416261629c..dc776834b5 100644 --- a/src/test/resources/META-INF/mods.toml +++ b/src/test/resources/META-INF/mods.toml @@ -71,3 +71,5 @@ loaderVersion="[28,)" modId="forgedebugmultilayermodel" [[mods]] modId="stencil_backport_test" +[[mods]] + modId="forge_spawnegg_test" \ No newline at end of file diff --git a/src/test/resources/assets/forge_spawnegg_test/models/item/test_spawn_egg.json b/src/test/resources/assets/forge_spawnegg_test/models/item/test_spawn_egg.json new file mode 100644 index 0000000000..fcdb62dea4 --- /dev/null +++ b/src/test/resources/assets/forge_spawnegg_test/models/item/test_spawn_egg.json @@ -0,0 +1,3 @@ +{ + "parent": "minecraft:item/template_spawn_egg" +} \ No newline at end of file