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

Minecart Variants

This page covers the minecart entity system in MinecraftConsoles, including the base Minecart class, the MinecartContainer abstraction, and each specialized variant.

Source files: Minecart.h/cpp

All minecart variants inherit from Minecart, which extends Entity. The entity type enum is eTYPE_MINECART.

ConstantValueVariant
TYPE_RIDEABLE0Player-rideable minecart
TYPE_CHEST1Chest minecart
TYPE_FURNACE2Furnace (powered) minecart
TYPE_TNT3TNT minecart
TYPE_SPAWNER4Spawner minecart
TYPE_HOPPER5Hopper minecart
IDFieldPurpose
17DATA_ID_HURTHurt animation time
18DATA_ID_HURTDIRHurt direction
19DATA_ID_DAMAGEAccumulated damage
20DATA_ID_DISPLAY_TILECustom display tile ID
21DATA_ID_DISPLAY_OFFSETDisplay tile Y offset
22DATA_ID_CUSTOM_DISPLAYWhether custom display is active

createMinecart() is a static factory that creates the right variant based on the type integer. Takes (Level*, double x, double y, double z, int type).

  • Collision: Minecarts have both a collide-against box (getCollideAgainstBox() for other entities pushing them) and a collide box (getCollideBox() for physics). They are pushable (isPushable() returns true).
  • Ride height: getRideHeight() sets the passenger offset.
  • Damage: hurt() animates the hurt effect and accumulates damage. destroy() is called when the minecart breaks; variants override this to drop their specific loot. animateHurt() triggers the visual hurt animation.
  • Movement: tick() handles interpolation, rail following, and natural slowdown. moveAlongTrack() does rail-specific movement using a static exit direction lookup table (EXITS[]). comeOffTrack() handles derailed movement with speed capping.
  • Activation: activateMinecart(int xt, int yt, int zt, bool state) is called when the minecart passes over an activator rail. The base implementation does nothing; TNT and hopper minecarts override it.
  • Entity pushing: push() handles minecart-to-entity and minecart-to-minecart collisions, with a m_bHasPushedCartThisTick guard (a 4J addition) to prevent double-pushing in one tick.
  • Natural slowdown: applyNaturalSlowdown() applies friction (protected virtual, overridden by container and furnace variants).
  • Pickability: isPickable() returns true (can be hit by the player).
  • Removal: remove() handles entity removal.

The static EXITS array stores exit direction vectors for each rail direction, as int[10][2][3]. This maps rail shape to the two possible exit directions, each with an X/Y/Z offset.

getPosOffs() and getPos() compute smooth interpolated positions along the rail for rendering.

lerpTo() and lerpMotion() handle smooth position and rotation interpolation over a given number of steps, using stored lx, ly, lz, lyr, lxr values. The lSteps counter tracks remaining interpolation frames. Motion interpolation uses lxd, lyd, lzd.

Each variant provides getDefaultDisplayTile(), getDefaultDisplayData(), and getDefaultDisplayOffset(). The display can be overridden at runtime via setDisplayTile(), setDisplayData(), and setDisplayOffset(), with hasCustomDisplay() tracking whether a custom override is active. setCustomDisplay(bool) toggles the override flag.

  • setCustomName(wstring) / getCustomName() / hasCustomName() for custom display names
  • getAName() returns the display name (custom name if set, otherwise default)

defineSynchedData() (protected) registers all six data IDs. 4J added makeStepSound() as a protected virtual returning false.

addAdditonalSaveData() / readAdditionalSaveData() handle base minecart serialization. The type and custom display settings are persisted.

  • getShadowHeightOffs() returns the shadow Y offset
  • flipped boolean tracks orientation
  • soundUpdater is a Tickable* for sound effects
  • getType() is pure virtual, each variant returns its type constant

Source files: MinecartContainer.h/cpp

MinecartContainer is an intermediate class that combines Minecart with the Container interface, providing shared inventory logic for the chest and hopper variants.

  • 36 slots initialized as ItemInstanceArray(9 * 4).
  • Max stack size: LARGE_MAX_STACK_SIZE (64).
  • All slots accept any item (canPlaceItem() always returns true).

Both destroy() and remove() go through all container slots and spawn the contents as ItemEntity instances. The remove() method checks a dropEquipment flag, which is set to false during dimension changes to prevent duplication.

applyNaturalSlowdown() adjusts the deceleration based on how full the container is:

emptiness = SIGNAL_MAX - getRedstoneSignalFromContainer(this)
keep = 0.98 + (emptiness * 0.001)
xd *= keep
zd *= keep

So a fuller container minecart slows down faster (lower keep multiplier). An empty container keeps 98% + 1.5% of its speed per tick, while a full container keeps only 98%.

interact() opens the container UI for the player on the server side via player->openContainer().

Items are saved as a "Items" list tag, with each entry containing a "Slot" byte and the item’s compound data.

Source files: MinecartChest.h/cpp

The chest minecart is the simplest container variant. Entity type: eTYPE_MINECART_CHEST.

PropertyValue
TypeTYPE_CHEST (1)
Container size27 slots (9 * 3)
Display tileTile::chest
Display offset8
Loot on destroyDrops a chest block
Container typeContainerOpenPacket::MINECART_CHEST

When destroyed, it calls MinecartContainer::destroy() (dropping all items) and also spawns a chest tile as loot via spawnAtLocation().

Source files: MinecartHopper.h/cpp

The hopper minecart combines MinecartContainer with the Hopper interface, so it can suck in items while moving. Entity type: eTYPE_MINECART_HOPPER.

PropertyValue
TypeTYPE_HOPPER (5)
Container size5 slots
Display tileTile::hopper
Display offset1
Loot on destroyDrops a hopper block

The hopper minecart has its own MOVE_ITEM_SPEED, set to half the tile hopper’s speed:

const int MinecartHopper::MOVE_ITEM_SPEED = HopperTileEntity::MOVE_ITEM_SPEED / 2;

This works out to 4 ticks (vs. the tile hopper’s 8 ticks), so minecart hoppers transfer items twice as fast.

activateMinecart() toggles the enabled flag. When the activator rail is powered, the hopper is disabled (newEnabled = !state). This matches vanilla behavior where a redstone signal turns hoppers off.

Each tick (server side, while alive and enabled):

  1. Decrements cooldownTime.
  2. If not on cooldown, calls suckInItems().
  3. On successful intake, resets cooldown to MOVE_ITEM_SPEED (4 ticks).

suckInItems() works two ways:

  1. Container above: Delegates to HopperTileEntity::suckInItems(this), which checks for containers in the block above.
  2. Loose items: Searches for ItemEntity instances in a slightly expanded bounding box (bb->grow(0.25, 0, 0.25)) and absorbs the first one found.

interact() opens the hopper UI via player->openHopper().

Saves and loads "TransferCooldown" in addition to the base container data.

Source files: MinecartFurnace.h/cpp

The furnace minecart is a self-propelled minecart that doesn’t have an inventory. Entity type: eTYPE_MINECART_FURNACE.

PropertyValue
TypeTYPE_FURNACE (2)
Display tileTile::furnace_lit
Display data2

The furnace minecart uses a simple fuel counter (int fuel) and tracks its push direction (xPush, zPush).

  • Synched data: DATA_ID_FUEL (ID 16) stores a byte indicating whether the furnace has fuel, synced to clients for rendering the lit/unlit state.
  • Adding fuel: Right-clicking (interact()) with coal adds TICKS_PER_SECOND * 180 ticks of fuel (3 minutes at 20 TPS = 3600 ticks). The push direction is set toward the player’s position relative to the minecart.
  • Tick: Each tick, fuel decrements by 1. When fuel hits zero, push forces are cleared. While fueled, large smoke particles spawn above the minecart (25% chance per tick).

moveAlongTrack() applies the push direction after the base rail movement. The push vector is normalized and aligned with the minecart’s current velocity direction. If the push opposes the current motion, the push gets zeroed out.

applyNaturalSlowdown() works differently from container minecarts:

  • Fueled: Velocity is dampened by 0.8, then a push force of 0.05 is applied in the push direction.
  • Not fueled: Standard 0.98 dampening.

When destroyed (not by explosion), the minecart drops a furnace block as loot.

TagTypePurpose
"PushX"DoubleX push direction
"PushZ"DoubleZ push direction
"Fuel"ShortRemaining fuel ticks

Source files: MinecartSpawner.h/cpp

The spawner minecart carries a mob spawner that runs while the minecart moves. Entity type: eTYPE_MINECART_SPAWNER.

PropertyValue
TypeTYPE_SPAWNER (4)
Display tileTile::mobSpawner

MinecartMobSpawner is a private inner class extending BaseMobSpawner. It bridges the spawner logic to the minecart entity:

  • getLevel() returns the minecart’s level.
  • getX(), getY(), getZ() return the minecart’s floored position (integer).
  • broadcastEvent(int id) sends entity events through the minecart.

The spawner field is a BaseMobSpawner* pointer to the inner class instance.

Two constructors: MinecartSpawner(Level*) for default creation and MinecartSpawner(Level*, double x, double y, double z) for positioned creation. The destructor cleans up the spawner.

tick() calls both Minecart::tick() and spawner->tick(), keeping the mob spawner active as the minecart moves.

handleEntityEvent() forwards events to the spawner’s onEventTriggered(), which handles client-side spawn particle effects.

getSpawner() returns the BaseMobSpawner* pointer.

The spawner’s data is saved and loaded via spawner->save(tag) and spawner->load(tag), embedding the spawner configuration directly in the minecart’s compound tag.

Source files: MinecartTNT.h/cpp

The TNT minecart explodes under various conditions. Entity type: eTYPE_MINECART_TNT.

PropertyValue
TypeTYPE_TNT (3)
Display tileTile::tnt
Fuse time80 ticks (4 seconds)
Event IDEVENT_PRIME (10)

The fuse field starts at -1 (not primed, set in the private _init() method). The minecart can be primed by:

  • Activator rail: activateMinecart() calls primeFuse() when state is true and the fuse isn’t already active.
  • Programmatic: primeFuse() sets fuse = 80, broadcasts the prime event, and plays the fuse sound. Priming is gated by the eGameHostOption_TNT game option.

Status checks: isPrimed() returns whether the fuse is active, getFuse() returns the current fuse value.

Each tick while primed (fuse > 0):

  1. Fuse decrements by 1.
  2. Smoke particles spawn above the minecart.
  3. When fuse reaches 0, explode() is called.

Also, if the minecart collides horizontally (horizontalCollision) with enough speed (speedSqr >= 0.01), it explodes immediately.

explode(double speedSqr) (protected) creates a level explosion with power based on speed:

speed = sqrt(speedSqr), capped at 5
power = 4 + random(1.5) * speed

The explosion can start fires (true parameter). The minecart is removed after exploding. If the TNT game option is disabled, the minecart is just removed without an explosion.

causeFallDamage(float distance) (protected) triggers an explosion when the fall distance is 3 or more blocks, with power scaled by (distance / 10)^2.

When destroyed by damage (destroy(DamageSource*)):

  • If TNT is disabled or the source is not an explosion: drops a TNT block.
  • If the source is fire, an explosion, or the minecart has speed: explodes.

When primed, the TNT minecart overrides two explosion methods:

  • getTileExplosionResistance() reduces explosion resistance of rail tiles to 0
  • shouldTileExplode() prevents rails from being destroyed by the explosion

This keeps the track intact beneath the detonation.

handleEntityEvent(byte) handles the prime event (ID 10) on the client side, triggering visual effects.

TagTypePurpose
"TNTFuse"IntCurrent fuse value (-1 if not primed)

Source file: MinecartRideable.h

The rideable minecart is the simplest variant with type TYPE_RIDEABLE (0). Its interact() method lets a player mount the minecart. No container, no special behavior, no additional save data.