Updated and ported everything to 1.21.1
Added:
Fixes a StackOverflowError crash (mutual disguises). Added a ThreadLocal<Boolean> recursion guard (sync$resolving). When Player A is disguised as Player B and vice versa, the first call to getModel()/getSkinTexture() sets the guard to true. When the mixin fires on Player B's entry and tries to chain back to Player A, it sees the guard is active and returns early, letting the original (non-disguised) skin/model through instead of recursing infinitely.
This also fixes the "chains through" bug where if A disguises as B who is disguised as C, A will get B's real skin, not C's. The guard ensures disguise resolution never follows more than one hop.
This update also fixes name tags not updating. The old code only checked for CustomName in NBT (never set for players), then fell back to the entity type translation key, showing "Player" for all player disguises. Now for player disguises it looks up the target player's name from the online tab list first, then falls back to sync$player_name stored in the disguise NBT. For non-player disguises the behavior remains unchanged. But it stores player name in disguise data (DisguiseManager.java + DisguiseAsPlayerAction.java).
DisguiseManager.applyDisguise: When the target is a PlayerEntity, stores sync$player_name in the NBT so the nametag mixin can display it and DisguiseAsPlayerAction.buildProfileNbt also stores the resolved profile name in the NBT, covering the offline-player disguise path
bientity_action field to Body Part Modifier Entry (Data Type) which allows you to execute bientity actions if a certain body part is hit.
A system for yielding new capabilities. Sync is an addon for the Apoli mod that's used primarily for the Origins mod!