Skip to content

These docs were made completely by AI, so they might be right, or wrong, you'll need to test them yourself. This was made for a easier understanding of everything. So use at your own risk. If anything is wrong, please don't hurt to make a PR on the page you have a problem with. ON GITHUB

Behavior System

MinecraftConsoles has a behavior system built around dispenser item behaviors. It’s based on a class hierarchy rooted in Behavior, with a registry that maps Item* pointers to their matching DispenseItemBehavior implementations.

Behavior
+-- DispenseItemBehavior (abstract: dispense())
+-- NoOpDispenseItemBehavior (static NOOP singleton)
+-- DefaultDispenseItemBehavior
+-- AbstractProjectileDispenseBehavior
| +-- ArrowDispenseBehavior
| +-- EggDispenseBehavior
| +-- SnowballDispenseBehavior
| +-- ExpBottleDispenseBehavior
| +-- ThrownPotionDispenseBehavior
+-- SpawnEggDispenseBehavior
+-- FireworksDispenseBehavior
+-- FireballDispenseBehavior
+-- BoatDispenseBehavior
+-- FilledBucketDispenseBehavior
+-- EmptyBucketDispenseBehavior
+-- FlintAndSteelDispenseBehavior
+-- DyeDispenseBehavior
+-- TntDispenseBehavior
+-- PotionDispenseBehavior

Key source files (all under Minecraft.World/):

FilePurpose
Behavior.hEmpty base class
DispenseItemBehavior.h/cppAbstract interface with dispense() and a static NOOP instance
DefaultDispenseItemBehavior.h/cppDefault: spawns item as a pickup entity on the ground
AbstractProjectileDispenseBehavior.h/cppFires a projectile via the abstract getProjectile() method
ItemDispenseBehaviors.h/cppAll concrete behavior subclasses
BehaviorRegistry.h/cppunordered_map<Item*, DispenseItemBehavior*> with a default fallback

Behavior is an empty class with no methods or fields. It exists purely as the root of the hierarchy.

The abstract interface for all dispense behaviors. Defines a single pure virtual method:

virtual shared_ptr<ItemInstance> dispense(BlockSource *source, shared_ptr<ItemInstance> item) = 0;

It also holds a static NOOP instance (NoOpDispenseItemBehavior) that does nothing and returns the item unchanged.

BehaviorRegistry is a simple map from Item* to DispenseItemBehavior* with a configurable default.

class BehaviorRegistry
{
unordered_map<Item*, DispenseItemBehavior*> storage;
DispenseItemBehavior *defaultBehavior;
public:
BehaviorRegistry(DispenseItemBehavior *defaultValue);
DispenseItemBehavior *get(Item *key); // returns default if key not found
void add(Item *key, DispenseItemBehavior *value);
};

The registry owns all registered behaviors and deletes them (along with the default) in its destructor. DispenserTile holds a static REGISTRY instance that the rest of the codebase queries.

When get() is called with an item that isn’t registered, the default behavior is returned. This means any item not in the registry will just get dropped on the ground.

DefaultDispenseItemBehavior defines a three-state outcome enum that drives sound and particle feedback:

OutcomeValueMeaning
ACTIVATED_ITEM0Special behavior ran successfully
DISPENCED_ITEM1Item dropped onto the ground as a pickup
LEFT_ITEM2Execution failed; item unchanged

The dispense() method calls execute(), then plays a sound and particle animation based on the outcome. Subclasses override execute() to do their specific thing and set the outcome accordingly.

When the outcome is LEFT_ITEM, a failure click sound (SOUND_CLICK_FAIL) plays instead of the normal click.

The default behavior drops the dispensed item as a pickup entity in the world. It:

  1. Gets the dispense position from the BlockSource.
  2. Gets the facing direction.
  3. Creates an ItemEntity at the dispense position with a small velocity in the facing direction.
  4. Decrements the source item stack.
  5. Sets outcome to DISPENCED_ITEM.

AbstractProjectileDispenseBehavior extends DefaultDispenseItemBehavior to fire projectiles:

  1. Checks Level::MAX_DISPENSABLE_PROJECTILES. If the limit is reached, falls back to DefaultDispenseItemBehavior::execute() (drops the item).
  2. Gets the dispense position from DispenserTile::getDispensePosition().
  3. Calls the pure-virtual getProjectile() to create the specific projectile type.
  4. Calls projectile->shoot() with the facing direction, adding a small Y offset (+0.1).
  5. Adds the projectile to the world and decrements the dispensed stack.
  6. Sets outcome to ACTIVATED_ITEM.

Default values:

  • Uncertainty: 6.0f
  • Power: 1.1f
  • Sound: SOUND_LAUNCH (instead of the default click)
ClassProjectile createdNotes
ArrowDispenseBehaviorArrowStandard arrow entity
EggDispenseBehaviorThrownEggStandard egg entity
SnowballDispenseBehaviorSnowballStandard snowball entity
ExpBottleDispenseBehaviorThrownExpBottleHalved uncertainty (*0.5), increased power (*1.25)
ThrownPotionDispenseBehaviorThrownPotionHalved uncertainty (*0.5), increased power (*1.25)

Each subclass overrides getProjectile() to return the right entity type.

Registration happens in DispenserBootstrap::bootStrap() (in Minecraft.Client/DispenserBootstrap.h), called during server initialization from MinecraftServer.cpp. It registers all 15 behaviors:

ItemBehavior class
Item::arrowArrowDispenseBehavior
Item::eggEggDispenseBehavior
Item::snowBallSnowballDispenseBehavior
Item::expBottleExpBottleDispenseBehavior
Item::potionPotionDispenseBehavior
Item::spawnEggSpawnEggDispenseBehavior
Item::fireworksFireworksDispenseBehavior
Item::fireballFireballDispenseBehavior
Item::boatBoatDispenseBehavior
Item::bucket_lavaFilledBucketDispenseBehavior
Item::bucket_waterFilledBucketDispenseBehavior
Item::bucket_emptyEmptyBucketDispenseBehavior
Item::flintAndSteelFlintAndSteelDispenseBehavior
Item::dye_powderDyeDispenseBehavior
Item::items[Tile::tnt_Id]TntDispenseBehavior

Hands off to ThrownPotionDispenseBehavior if PotionItem::isThrowable() returns true for the aux value. Otherwise falls back to DefaultDispenseItemBehavior (drops a non-splash potion on the ground).

Spawns the entity via SpawnEggItem::spawnMobAt(). If the spawn limit is reached (entity == nullptr), sets the outcome to LEFT_ITEM. Transfers the custom hover name to the mob if one is set on the dispensed stack.

Checks Level::MAX_DISPENSABLE_PROJECTILES before spawning a FireworksRocketEntity. If the limit is hit, sets LEFT_ITEM.

Checks Level::MAX_DISPENSABLE_FIREBALLS. Creates a SmallFireball with a random Gaussian spread on the direction vector. Plays SOUND_BLAZE_FIREBALL on success.

Checks Level::MAX_XBOX_BOATS. Spawns a Boat only if water is in front of the dispenser (or air with water below). If neither condition is met or the boat limit is reached, falls back to defaultDispenseItemBehavior->dispense() (drops the item).

Ignites air blocks in front of the dispenser or activates TNT blocks. Damages the flint-and-steel item on use. If the target block is neither air nor TNT, sets LEFT_ITEM.

When the dye aux value is DyePowderItem::WHITE (bone meal), applies growCrop() to the block in front. Non-white dyes fall through to DefaultDispenseItemBehavior.

Checks Level::newPrimedTntAllowed() and the game host TNT option before spawning PrimedTnt. If either check fails, the TNT is not placed.

Places water or lava source blocks in front of the dispenser. The empty bucket replaces the filled one in the dispenser slot.

Picks up water or lava source blocks in front of the dispenser. The filled bucket replaces the empty one in the dispenser slot.

Comments tagged 4J-JEV throughout the code mark console-edition-specific changes:

  • The eOUTCOME parameter was added to execute() to support failure sound effects when spawn limits are hit.
  • Spawn limits (MAX_DISPENSABLE_PROJECTILES, MAX_DISPENSABLE_FIREBALLS, MAX_XBOX_BOATS) are console-specific caps that don’t exist in Java Edition. These prevent performance issues on consoles.
  • The BlockSource abstraction wraps the level and position data for the dispenser.

LCEMP does not have the behavior registry, DispenseItemBehavior, BehaviorRegistry, DispenserBootstrap, or any of the concrete dispense behaviors. The dispenser tile itself is not implemented in LCEMP either. This entire system is exclusive to MinecraftConsoles.