feat(events): fire Bukkit EntityBreedEvent for animal breeding (1.21.11) - #577
Open
KostiaFed wants to merge 1 commit into
Open
feat(events): fire Bukkit EntityBreedEvent for animal breeding (1.21.11)#577KostiaFed wants to merge 1 commit into
KostiaFed wants to merge 1 commit into
Conversation
Cardboard already had CraftEventFactory.callEntityBreedEvent, but the only caller was VillagerMakeLoveMixin, so no vanilla animal ever produced a Bukkit EntityBreedEvent. Plugins that track breeding (AdvancedAchievements and friends) therefore never saw cats, sheep or anything else reproduce. Hook Animal#spawnChildFromBreeding right before finalizeSpawnChildFromBreeding: the child exists there, but the parents' love/age are not reset yet, no experience has dropped and the child is not in the world, so cancelling the event leaves the world untouched. This is the same point CraftBukkit patches. Fox breeding needs its own hook because Fox$FoxBreedGoal overrides BreedGoal#breed() and spawns the kit itself (to copy trusted players) instead of calling spawnChildFromBreeding. Frog, Sniffer and Turtle are deliberately left alone: they lay an egg and never create a child entity, matching Bukkit/Paper behaviour. - mother/father/child/breeder are taken from vanilla's own love-cause chain - breedingItem mirrors CraftBukkit's Animal#breedItem, captured in mobInteract and exposed through the new AnimalBridge (may be null, which Bukkit allows) - experience is offered to the event and honoured afterwards; 0 drops no orb - breeding without a player yields a null breeder instead of skipping the event Backport of the ver/26.1 change. The vanilla breeding code is identical in 1.21.11 and 26.1.2 down to the bytecode offsets of spawnChildFromBreeding, finalizeSpawnChildFromBreeding, Animal#mobInteract and Fox$FoxBreedGoal#breed, so every injection point applies unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backport of #576 to
ver/1.21.11.Problem
CraftEventFactory.callEntityBreedEvent(...)already exists on this branch, but its only caller isVillagerMakeLoveMixin. No vanilla animal ever produces a BukkitEntityBreedEvent, so any plugin that tracks breeding (AdvancedAchievements was the reported case, but this affects every such plugin) silently sees nothing when cats, sheep, cows, … reproduce.Where the hook goes and why
Breeding funnels through
Animal#spawnChildFromBreeding:Injecting right before
finalizeSpawnChildFromBreedingis the only point where the child already exists but nothing is committed yet — parents are still in love, ages are not reset, no experience has dropped, and the child is not in the world. Cancelling there leaves the world exactly as it was. This mirrors where CraftBukkit patches the same method.Callers covered:
BreedGoal#breed()and the brain-basedAnimalMakeLove, i.e. every animal that produces a child entity.Why foxes get their own mixin
Fox$FoxBreedGoaloverridesBreedGoal#breed()and never callsspawnChildFromBreeding— it creates the kit itself so it can copy both parents' trusted players. Without a dedicated hook, foxes would be the one vanilla mob that spawns a baby without anEntityBreedEvent.Frog,SnifferandTurtleare deliberately left alone: they lay an egg and passoffspring == null, so there is no child to report — same as Bukkit/Paper.Event semantics
this, the partner, and the freshly created offspring.getLoveCause()of the mother, falling back to the father's; the same source vanilla uses to award theANIMALS_BREDstat. Breeding with no player involved fires the event with anullbreeder rather than skipping it.Animal#breedItem, captured inmobInteractjust before the food is consumed and exposed to the fox goal through the newAnimalBridge. May benull, which the Bukkit API allows.setExperience(0)drops no orb at all. Still gated on the vanillaMOB_DROPSgame rule.Relation to the 26.1 change
This is the same patch as #576, applied unchanged. The vanilla breeding code is identical between 1.21.11 and 26.1.2 — same packages (
animal.fox.Fox$FoxBreedGoal,animal.feline.Cat,animal.sheep.Sheep), same method descriptors, and the same bytecode offsets inspawnChildFromBreeding(0→47),finalizeSpawnChildFromBreeding(0→113),Animal#mobInteract(the twousePlayerItemcalls at 49 and 77) andFox$FoxBreedGoal#breed. Every@Attarget and ordinal therefore applies verbatim; the cherry-pick was conflict-free.Testing
The behaviour was verified on a live Fabric 26.1.2 server (loader 0.19.2, fabric-api 0.149.1) with a probe plugin listening on
EntityBreedEvent:child=SHEEP mother=SHEEP father=SHEEP breeder=null exp=3BreedGoalpath as cats)child=OCELOT … exp=1child=FOX … exp=5setExperience(0)setExperience(37)37InvalidMixinException, clean bootNot exercised headlessly: the non-null
breeder/bredWithpath, which needs a real player feeding the animals. A 1.21.11 build and in-game run are still to be done on this branch — happy to hold the PR until someone confirms.