-
Notifications
You must be signed in to change notification settings - Fork 200
Handling levels, maps and chunks
A major part of MiNET's performance relies on levels, and their performance. Due to this, you must pay a lot of attention to your worlds and how you handle them, the page documents most of the important factors while doing this, however, there is still some general information that you are best off knowing. So, optimizing maps is very important due to the fact that bandwidth is a major factor, as it can be very expensive, especially with a large network of players. A single extra wasted chunk of air is 280 bytes. This means that for every player that loads this map, you are sending a useless 280 extra bytes, for every chunk, for no reason. On a large network this can amount to huge amounts, for example, let us say that we have a server with 8000 players online, and we have forgotten to prune 200 extra chunks of air from the lobby world. We can determine that just from players joining the lobby, we have wasted 448 megabytes ((280 * 200 * 8000)/1000000). Not only have we wasted bandwidth for ourselves, we have wasted bandwidth for the players, and CPU usage on both ends too. This is just for a single map, too.
In MiNET, Worlds go under the name levels. Levels are created very simply, and users can be sent to and from them very simply too with the Player#SpawnLevel method. Below is a simple example of method creation
var provider = new AnvilWorldProvider("C:\....");
var level = new Level("example", provider);
level.Initialize();
player.SpawnLevel(level);
A more advanced example of this can be seen here: https://gist.github.com/Polo1K/25dd5a3abc2f10bf7fe2962e97bb5dec
As a major factor of MiNET is it's performance, it is given that you want to keep that performance just as high as we've kept that. Levels & maps are a huge consideration when it comes to performance, and it is one of the things you should first think of when diagnosing performance issues. Below are some methods for optimizing your levels, and your maps, to keep up performance.
It's important to only send as many chunks as required, and chunks that are not going to be used should not be sent! There are many different ways of handling this, like heading in to MCEdit and cutting out non-required chunks that are full of air and so on, but we have implemented two methods that you should definitely take in to consideration in the AnvilWorldProvider. The first method is PruneAir, this removes all non-required air chunks (certain air chunks are required, read more about this in issues). The second method is MakeAirChunksAroundWorldToCompensateForBadRendering (a little long 😆), this will place a frame around your map to prevent chunk issues with chunks with blocks in them not visually appearing due to a bug in MCPE (this can also be read about this in issues).
In general, it is best to avoid player deaths. This means that the map never has to be re-sent to the player, lowering the likeliness of visual bugs on his end, and lowing the bandwith and CPU usage on your end. This can be dealt with in the HealthManager for the player, by simply overriding it along with the Kill method.
Obviously, grabbing a new provider for each level every time can be a massive drain on resources, especially due to the fact that it is largely based upon I/O. So, the trick is, is to keep a copy of a world provider that can be used for when you need it again. This is essential for networks where you are using the same map multiple times, like in Survival Games, or any mini-game to be frank. So, the trick is, is to create your own "LevelFactory" where you store copies of world providers for when you need them.
To do this, you must keep a dictionary of the directory, with the provider aside it. Create a method to check if the dictionary contains the directory passed to it, and if not, it must create the provider, add it to the dictionary, and return it for next time. The benefit of this is that the provider will only be created once. It is important that when you want to get a provider that you have cached, that you provide a clone of the provider if it is to be built upon (with blocks), otherwise you're going to see block updates throughout levels and this will create large amounts of issues. It is fine to return the provider itself however if the map is not to be edited. For example you would return a clone of the provider for a game like Spleef where the map is constantly edited by players, but just the provider itself for a game like Survival Games where the map is never edited. It is generally best to create games that avoid interaction with the terrain, as the performance is vastly improved by simply returning the provider itself than a clone.
It is important to keep the view distances for levels at a low amount, as the higher it is the more chunks that your server will have to send to the clients, and the more chunks that it will have to keep cached for each map. We recommend a chunk distance of 7-11 at maximum, and this can be set at Player#MaxDistance. Mileage may very depending on the performance of your server/computer, and the amount of players on the server. For scale, Windows 10 at the maximum view distance will request 22 chunks from the server, that is over 2000 chunks. The minimum size for a world is 56 chunks (not view distance!), any lower than this and the client will be unable to spawn in to the world.
A major issue, and something to take in to consideration is how MCPE renders chunks. In order for MCPE to be able to render chunks that contains blocks, it MUST have a surrounding frame of chunks, a diagram of this can be seen below. In order to deal with this, we have two methods in the AnvilWorldProvider, PruneAir & MakeAirChunksAroundWorldToCompensateForBadRendering, although this is mentioned below, I will mention it again as it is rather important. PruneAir removes all unused air chunks in the world (air chunks that do not have chunks surrounding them that have blocks inside of them), this lowers bandwith & CPU usage, and MakeAirChunksAroundWorldToCompensateForBadRendering adds air chunks to all chunks with blocks within them. This means that you do not have to do this yourself with a tool like MCEdit. Note that if you can see the lit up part of the blocks that are invisible this is your problem, but if the chunks aren't even there then this is not the problem.
This diagram, (known in the MiNET community as a Boobogram) shows how all populated chunks are surrounded with air chunks when calling MakeAirChunksAroundWorldToCompensateForBadRendering.