MineRoyale.io Block Data Algorithm

1/14/19

Storage on the Server

The block data is stored on the server in large 2D array. The whole map is usually about a 200x200 2D array! However, only once do I ever iterate through that whole array: When I create the map. In all other instances, I figure out the index that I should be concerned with based on the position of the player/entity, and then simply work at or near that index. For example, if TNT blows up, I locate that position, convert it from world coordinates to block indices (1 block length is 128 in world coordinates), then choose lower/upper bound indices above and below the specific index, usually of about 4, and decide whether the blocks in that range should either be removed or kept.

Sending to Clients

Each tick of the server, the view frame of the player when it comes to blocks may change. Rather than sending all the blocks in the game, I simply send the blocks in a range near the player, building a 2D array specific to that player. Since the expected screen resolution of the game is 16:9, the 2D array built specifically for that player is also about that ratio in blocks, like 12x8, because that is all that should be visible to the player. Each tick this array is recreated, as some block in the players view may have changed. This is then sent to the player to render the blocks, or keep them the same if they have not changed.

Darkness Blocks

Not all blocks that are stored within that 12x8 frame should be visible to the player. Instead, we take the block that the player is standing on and work our way in all directions to see if adjecent blocks can be walked on and seen through. If not, the search for new blocks is not continued from those blocks. Blocks which are not reached have their type changed from their original type to the darkness type before being sent to this player. In this way, the player has no idea what is behind a stone wall, and the network data will not even reveal it, because it is replaced by default darkness blocks on the server.

Comparing Frames

When a player is not moving, it is typical for the view frame to not change at all. In these cases, it would be wasteful to resend the data. Instead, the server creates the new frame, but compares it with the saved old frame. If they are exactly the same, the newly created data is not sent.

Reducing Message Size while Stationary

The second most common case of block change is for only one or two blocks to change in the players frame. In these cases, rather than sending the whole 12x8 array of data, the server figures out exactly which blocks changed, and sends those, rather than the entirety of the 2D array. Then, the client makes those changes without editing any other blocks.

Reducing Message Size on Movement

The last case for which I improved efficiency was the case where the player moves one block down, up, right, or left. This case is particularly hard because, although I know I have just shifted everything, my frame comparing algorithm does not see that. All it sees is a 12x8 frame compared to the old 12x8 frame, which is completely different on a block-by-block basis. In these cases, I form a short array which contains the new data that is revealed to the player. If the player moves up or down, this will be a 12 element array, because that is how many blocks were revealed above or below them. If they move left or right, it will be an 8 element array. The client takes this array, pushes it in where it goes, and removes one layer of elements on the other end. Of course, this method must be combined with the method of sending individual block-by-block changes as described previously. The reason for this is that a player could potentially move by one block in a frame, and a block could change because they mined it in that exact same frame.

Conclusion

All of these methods of data storage and transfer were created to make the game server fast and the data transfer as efficient as possible. No data should be sent twice if it doesn't have to be. It is more complex than it would initially seem to have really efficient transfer of block data, because there are so many cases where very little is changing, but can be interpreted as a lot of change when considered naively.

Want to learn more? Join the MineRoyale.io Discord to chat!