Skip to main content

Player Disguises

FreeMinecraftModels can disguise any online player as a loaded model. Disguises are driven by commands, a public DisguiseAPI, and a per-player PlayerDisguiseEntity that handles invisibility and model rendering for the disguised player.

Commands​

CommandPermissionNotes
/fmm disguise <modelID>freeminecraftmodels.disguise.selfDisguises the sender. Player-only
/fmm disguise <modelID> <player>freeminecraftmodels.disguise.self + freeminecraftmodels.disguise.othersDisguises the named player. Works from console
/fmm undisguisefreeminecraftmodels.disguise.selfRemoves the sender's disguise. Player-only
/fmm undisguise <player>freeminecraftmodels.disguise.self + freeminecraftmodels.disguise.othersRemoves the named player's disguise. Works from console
/fmm disguiselistfreeminecraftmodels.disguise.othersLists every disguised player and the model each is disguised as

The freeminecraftmodels.* wildcard grants admin, deleteall, disguise.self, and disguise.others at OP default. It also grants the separately registered freeminecraftmodels.bypassregionprotection permission.

Behavior​

  • Disguising a player who is already disguised cleanly replaces the previous disguise — no need to undisguise first.
  • FMM hides the player from other viewers with setVisibleByDefault(false) and adds an invisibility potion effect if the player does not already have one. Equipment overrides hide armor and held items from other viewers, without changing the inventory. Self-view is excluded from those equipment overrides; the client's own body rendering still depends on its visibility behavior.
  • FMM re-applies invisibility one tick after relevant effect-removal/change events or respawn only if this disguise originally added the effect. It does not preserve or restore an invisibility effect that already existed when the disguise started. Removing the disguise sets default visibility back to true, removes an FMM-owned effect, and resends the player's current equipment.
  • Equipment hiding is resent for joining viewers and world changes. This does not replace the player's actual armor or held items.
  • The player's hitbox is unchanged. The decorative disguise does not create a packet interaction entity and its click handlers do nothing, so it does not provide a separate target for attacks or mounting. Invisibility can still affect behavior outside FMM; unchanged mob targeting is not guaranteed.
  • Disguises do not survive a server restart, a /fmm reload, or the player logging out — they are tracked in memory and the DisguiseListeners.onQuit handler removes the disguise on PlayerQuitEvent. Death and world changes do not undisguise the player.
  • A successful /fmm reload tears down active disguises during shutdown; players need to be re-disguised afterward. A reload rejected during candidate-content preparation retains the active runtime.

Disguise Animations​

Each disguise has a controller that selects animations from the player's state before the shared modeled-entity playback engine runs. It does not bypass that engine. Reserved names must be lowercase because the controller's animation-existence checks are case-sensitive.

Five reserved animation names are recognized, in this priority order (highest first):

PriorityAnimationTypeTriggered by
1attackone-shotThe player swings their arm
2jumpone-shotThe player leaves the ground moving upward
3sneakloopThe player is sneaking
4walkloopThe player's position moved more than 0.01 blocks since the last tick, including vertical movement
5idleloopNothing else applies

Behavior worth knowing:

  • Only animations the model actually has are considered. A model with no sneak animation simply falls through to walk or idle.

  • A one-shot requests immediate, non-looping playback and pauses all new selections for a fixed 20 ticks (1 second at 20 TPS). This is a countdown, not the animation's measured duration. A longer animation can be interrupted by the next selection; a shorter one can finish before selection resumes. Arm swings during the pause collapse into one pending attack, while jump transitions during it are not queued.

  • Loops are only re-issued when the chosen animation actually changes, so a steady walk is not restarted every tick.

  • jump is genuinely wired up for disguises, unlike the (currently inert) jump state on regular modeled entities. See Animations.

  • A disguise model with no idle animation logs a warning on every disguise:

    Disguise model '<model>' has no 'idle' animation — disguised players using this model will freeze on the last animation frame whenever no other action is active.

    Ship an idle animation with any model you intend to use as a disguise.

Bedrock Clients​

Disguises rely on FMM rendering the custom model. The default sendCustomModelsToBedrockClientsV2: true in config.yml enables Bedrock model sending. Setting that key to false prevents the model from reaching Bedrock viewers while the disguised player remains invisible. The retired sendCustomModelsToBedrockClients key is no longer read. Even with V2 enabled, the client needs the converted resource pack and a working Geyser/Floodgate integration.

DisguiseAPI​

Third-party plugins can drive disguises programmatically via com.magmaguy.freeminecraftmodels.api.DisguiseAPI.

import com.magmaguy.freeminecraftmodels.api.DisguiseAPI;

// Disguise (returns false if the model ID is not loaded)
boolean ok = DisguiseAPI.disguise(player, "dragon");

// Undisguise (returns true if a disguise was removed)
DisguiseAPI.undisguise(player);

// Queries
boolean isDisguised = DisguiseAPI.isDisguised(player);
String modelID = DisguiseAPI.getDisguiseModelID(player); // null if not disguised

// Unmodifiable snapshot of all disguised players
Collection<Player> all = DisguiseAPI.getDisguisedPlayers();

The API is the public, refactor-safe entry point. Plugins should call DisguiseAPI rather than the internal DisguiseManager.

The API does not perform command permission checks or model-ID normalization. Pass an online player and an exact loaded ID. disguise removes the existing disguise before trying to create its replacement, so an unknown ID returns false after removing the old disguise. The command checks the model first, preserving the previous disguise on an invalid command ID. A successful API return establishes creation, not client rendering or resource-pack acceptance.

Common Use Cases​

  • Roleplay and RPG servers: disguise players as boss models for cinematics or quest scripts.
  • Event hosting: disguise an announcer as a giant custom model.
  • Mounts and vehicles: combine /fmm mount with /fmm disguise for creative transport setups.
  • EliteMobs integration: boss scripts can disguise the player who triggered an event for a temporary transformation effect.

Troubleshooting​

Nothing happens when I run /fmm disguise dragon. The model ID must match a loaded model exactly. Check /fmm admin or the command's tab completion to verify the model name. The targeted form checks freeminecraftmodels.disguise.others before resolving the model or player and reports that missing permission directly.

My disguise vanished after /fmm reload. Expected after a successful full reload. Listen for FmmReloadedEvent, scheduled on the main thread 40 ticks after initialization, if you need to re-apply disguises automatically. A rejected preparation step does not tear down the active runtime.

Bedrock players cannot see the disguise. See Bedrock Clients above.

The disguised player is still visible to others. Check whether the player already had invisibility when the disguise started: FMM does not take ownership of that effect and will not restore it if it disappears. Also check for plugins changing player visibility, effects or equipment packets. FMM uses both player visibility and a real potion effect, plus equipment overrides; confirm the conflicting behavior before changing another plugin's settings.