Wednesday 7 January 2015

Summary of logic flow when mining blocks

This section gives an overview of the logic flow in the block mining code.

Block damaging and harvesting occurs in several stages:
  1. Make an initial check to see if block damaging should start or if it should be aborted immediately.
  2. Incrementally damage the block (updating of the block damage animation, send to clients).  The rate of damage per tick is calculated from properties of the block and the item hitting it.  If the rate is 0, the block is invulnerable to damage.
  3. Once the block damage reaches 1.0 (i.e. 100%), attempt to break the block, and if successful, harvest the block (drop items).
There are three different ways to modify the vanilla behaviour:
  • Events (suitable for all Items and all Blocks)
  • Overriding a Block method (for custom mod Blocks)
  • Overriding an Item method (for custom mod Items)
These are described in the detailed flow description below:

Initial Checks

1. PlayerInteractEvent(LEFT_CLICK_BLOCK):
  • if the event is cancelled, abort
  • if event.useItem == DENY, abort.
2. If this is in Adventure Mode:
  • check if the equipped itemstack has the NBT "CanDestroy" tag including the given block - see ItemStack.canDestroy().  If no tag, abort.
3. If this block is part of the world border (see WorldBorder) - abort

Ongoing Damage

  • keep track of the number of ticks that the player has been digging for
  • calculate Block.getPlayerRelativeBlockHardness() which is the block_damage_per_tick.
  • once the ticks * block_damage_per_tick reaches >= 1.0, try to break and harvest it

Attempt to Break and Harvest Block

1. BlockEvent.BreakEvent
  • if cancelled, abort without destroying the block
2. Item.onBlockStartBreak()
  •  if returns true, abort without destroying the block
3. Play breaking sound.
4. Block.canHarvestBlock()
  • checks to see if items can be harvested when this block is broken
  • by default, Block.canHarvestBlock() calls ForgeHooks.canHarvestBlock() – see below.
  •  if returns false, no blocks will be harvested.
  4a ForgeHooks.canHarvestBlock()
  • If the block material doesn’t require a tool, return true
  • If the Block.getHarvestTool() has a particular ToolClass, the Item has a matching ToolClass, and the harvest level of the Item is >= the Block harvest level, then return true.
  • Otherwise, raise PlayerEvent.HarvestCheck event to decide (defaults to false).
5. Item.onBlockDestroyed()
  •  used to damage the item – by default 1 for tools, 2 for sword
  • if returns true, updates achievement information
6. Block.onBlockHarvested()
  • Used to drop extra items (eg skulls) or destroy other parts of a multiblock (eg bed)
7. Block.removedByPlayer()
  • Overwrites the block – by default sets it to air
  • If it returns true (default) then it also calls:
  7a. Block.onBlockDestroyedByPlayer()
  • this method mostly does nothing but some add extra effect- eg TNT generates an explosion
8. If the previous calls to either Block.canHarvestBlock() or Block.removedByPlayer() returned false, skip the harvesting steps.  Otherwise:
9. Block.harvestBlock(); by default it does the following:
  9a. If Block.canSilkHarvest() and silk touch enchantment then
  • Block.createStackedBlock() to create an item from the block
  • BlockEvent.HarvestDropsEvent with silktouch=true to modify the list of dropped items if desired
  • Spawn the items
  9b. If not silk harvesting, then Block.dropBlockAsItem() --> Block.dropBlockAsItemWithChance(), by default this does:
  • Block.getDrops() to return a list of all possible items dropped by this block
  • BlockEvent.HarvestDropsEvent with silktouch=true to modify the list of possible items (if desired) and the harvesting chance
  • Spawn one or more of the items from the list, random chance

Client<-->Server synchronisation

The Server receives three signals from Client

  • C07Packet START_DESTROY_BLOCK (start destroying block)
  • C07Packet STOP_DESTROY_BLOCK (destroy block if ready)
  • C07Packet ABORT_DESTROY_BLOCK (abort)

The block digging calculations are performed on the client as well as the server, but most of the methods in the Break and Harvest section are called on the server only; the final result is sent to the client (i.e. block changes and item changes).
The code is designed to be relatively robust to synchronisation mismatches due to lag; the server has the final say on whether a block is destroyed or not, but depending on timing the methods can be called repeatedly on blocks which are already destroyed, for block positions which do not exist, or for "air" blocks instead of the actual block.  The vanilla code ignores these "glitches", but it means that your code should not rely on a strict sequence of method calls to function correctly.

Further Links

~Mining Blocks With Tools
    Logic flow of vanilla block mining code
    Calculating rate of damage when mining

No comments:

Post a Comment