1
\$\begingroup\$

I am wondering, how does minecraft handle blocks like chest?

I guess normal blocks are ids in simple array, but how does it store and references blocks with special behaviour?

\$\endgroup\$
  • 1
    \$\begingroup\$ What are you trying to accomplish in your game or mod? \$\endgroup\$ – DMGregory Jun 21 '19 at 18:43
1
\$\begingroup\$

Both Mark Mueller and Ratchet Freak have part of the full answer.

  1. Minecraft blocks are singleton instances: there is only one instance of the BlockDirt class, one instance of the BlockChest, only one BlockStone, and so on. There are a few exceptions due to relatively one-off effects (such as redstone ore and furnaces casting light "sometimes"), but not important here.

  2. Because there's only one instance all block functions are passed: A World, a position, and any other information necessary (such as the Entity, Hand, or ItemStack involved). This information is all that is needed by the block class to do pretty much anything under the various conditions that a block is subject to (which ranges widely across multiple aspects of the game, including rendering).

  3. Blocks that have extra data or extra functionality use TileEntities to store it or process it (as Ratchet points out). This includes the Chest, Furnace, Daylight Sensor (iirc), Hopper, and others. Any block that can "hold" an item in it has an associated tile entity.

    • Same goes for blocks that "tick" (such as furnace smelting) as regular non-TE blocks only get random ticks (this is why wheat does not all grow at the same speed, despite identical conditions). Note that while a TE is required for ticking and a TE is required for an inventory (or other extra data), TEs do not need to do both (but frequently will).
    • The Crafting Table does not have a TE however: when the UI is closed all of its "contained" items pop back into your inventory because there is no 'container' to store them in. Blocks themselves declare whether or not they have a TE and if so, create it.
    • The BlockContainer class is not actually relevant. It was just a intermediate class to override a few methods that "all" container blocks are likely to need (but this actually messes up modders because it does things some container blocks don't need...such as completely disabling rendering because the chest is rendered in special edge-case code, but that disabling was done in BlockContainer irrationally and re-overriden in the non-chest blocks). Mark alludes to this class, but its purpose is contrary to what he actually said.

From here we have everything we need: as Mark said, blocks do a sort of "background check" when their functions are run (bullet 2) although what that logic is varies from block to block. For blocks like Chests (Crafting Table, Hoppers, Enchanting Table and so on) when they are right clicked the code says, "Hey, we need to open a UI" and that's all that's in the block code. Essentially:

SomeBlock extends Block {
    public void onRightClick(World w, BlockPos p, EntityPlayer player) {
        player.openUI(w, p, Constants.CHEST_UI);
    }
}

Not exactly what vanilla does, but close enough. Vanilla essentially says "hey, open the Chest UI" and the server goes "cool, I know what bits I need (player, world, and position) and I also need to send a packet to that player's client and then the client will do what it needs to." Forge adds a similar method for mod code that includes what mod is making the call so Forge knows where to find the right classes to invoke.

The hidden underlying background code that gets called some were along the way is world.getTileEntity(world, pos) and this is what converts from a "location in the world" to "that specific tile entity that is stored at that location." Blocks are stored in a massive array-of-arrays-of-arrays-of-arrays of integers (exact implementation varies by Minecraft version), which is why worlds can be so huge without actually taking up that much ram/hard drive space. TileEntities by contrast are stored in a hashtable (for each chunk) mapping position to TE instance as relatively few block positions contain a TE (and things go really really bad when they do! I've done it by accident).

So when a Block (a singleton) has a method called that needs to interact with the extended data held in a TE, that block can get that TE from the world and position parameters it gets from its own function call. If a UI is involved, then the player parameter comes into play, and from there network communication gets triggered in order to display the GUI on the client.

For blocks like the chest, there are two additional classes involved: a Container class and a GuiContainer class (thought that name is a bit misleading as the TE may not actually store items). The container holds information about the inventory of the block (TE) and one deals with actually displaying that information to the player. As such there are two instances of the Container present: one on the server (to track changes coming from the client and apply them to the TE) and one on the client (in order to know how to display the TE's data and send any changes to the server). How these classes work is incredibly complicated and I don't even know all of it. Even some of the functions like "player shift-clicks an item stack" is a convoluted mess that I don't fully understand. I just know that it's needed and that those 60 lines work correctly (and don't know why it isn't in the vanilla parent class, but instead implanted new in each of the Container subclasses that deals with inventories).

And that should give you at least a broad understanding of how it works in general and how incomplete even my answer is.

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

There is a separate "TileEntity" list per chunk which store the data for blocks that don't have enough with the id+metadata that is available for all blocks in the chunk.

When the chunk gets loaded or the block placed the id of the block maps to a class that knows whether it needs that special data and will load and parse it.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

It's been a while since I have looked at the minecraft source code but I think the block extended a container handler. Whenever you right clicked it will search if the right click happened on the block and if it had a functionality it will run that code.

So it basically does a background check for the object you right clicked on and ran that blocks code.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.