Difference between revisions of "Engine/NMPR"

From Minetest Developer Wiki
Jump to navigation Jump to search
Line 90: Line 90:
 
This is how Minetest works up to this day, except:
 
This is how Minetest works up to this day, except:
 
* At some point, instead of storing faces, Minetest was made to store [http://en.wikipedia.org/wiki/Polygon_mesh meshes].
 
* At some point, instead of storing faces, Minetest was made to store [http://en.wikipedia.org/wiki/Polygon_mesh meshes].
* In Minetest 0.3.1, [http://en.wikipedia.org/wiki/Hidden_surface_determination occlusion culling] was added to the render step
+
* In Minetest 0.3.1, [http://en.wikipedia.org/wiki/Hidden_surface_determination#Occlusion_culling occlusion culling] was added to the render step
 
* In Minetest 0.4.3, the list of MapBlocks to be rendered is cached, and sorted by texture
 
* In Minetest 0.4.3, the list of MapBlocks to be rendered is cached, and sorted by texture
  
 
== Hmm... ==
 
== Hmm... ==

Revision as of 09:46, 20 January 2013

The base (NMPR)

Everything is built on a small core, that was the original network multiplayer release of Minetest (let's call it NMPR; the 2010-10-24 version). Being around 10000 lines of code, it contains:

  • The Map: Voxel storage + lighting + rendering
  • The Client + Server logic
  • The Environment: Contains the map and the players, handles the simulation of the world
  • The main loop: Invokes the client, the server, the environment and the rendering.
  • A bunch of wrappers for OS-dependent things, and utilities.

As the current code still largely bases on the NMPR, it is useful to look at how it works.

Map (the voxels)

Minetest Voxel Storage
  • The main content of the Map is a map<v2s16, MapSector*> container.
  • The main content of a MapSector is map<s16, MapBlock*>.
  • The main content of a MapBlock is a linear array of 16x16x16 MapNodes.

These form a relatively performant storage of voxel data. In addition to these containers, the latest fetched MapBlock is cached in each MapSector, and the last fetched MapSector is cached in Map, resulting in very useful sequential access speed through the whole abstraction layer.

Network protocol

The high-level network protocol of NMPR is delightfully simple. There are four commands for the server, and four commands for the client. Since this, a lot has been added and changed, but the basic idea stays the same.

Client -> Server

TOSERVER_GETBLOCK v3s16 p Ask the server to send the data of a block
TOSERVER_ADDNODE v3s16 p, MapNode node Inform the server of a placed node
TOSERVER_REMOVENODE v3s16 p, MapNode node Inform the server of a removed node
TOSERVER_PLAYERPOS v3s32 p*100, v3s32 speed*100 Inform the server of the positon of the local player

Server -> Client

TOCLIENT_BLOCKDATA v3s16 p, MapBlock data Send the content of a block (16x16x16 nodes)
TOCLIENT_ADDNODE v3s16 p, MapNode node Add a node
TOCLIENT_REMOVENODE v3s16 p, MapNode node Remove a node
TOCLIENT_PLAYERPOS foreach(player){
u16 player_id, v3s32 p*100, v3s32 speed*100}
Update players on client

Minetest uses it's own reliability layer on top of UDP. It isn't well documented at the moment, and thorough understanding of it isn't that important, so let's skip it as of now.

Environment

The content of the environment is:

  • Map
  • List of Players

Players in NMPR contain:

  • Position and speed
  • An Irrlicht scene node (that is rendered by Irrlicht)
  • move() method with collision detection

Rendering

Rendering of players and UI is done by regular Irrlicht stuff (Irrlicht tutorials). What is interesting is how the voxel world is rendered.

In NMPR, rendering of the Map works like this:

Cache step (runs in the background, in a thread managed by Map)

  • List MapBlocks in displayed area that have been modified
  • Update lighting in them
  • foreach(MapBlocks that were modified in the previous steps):
  • Go through the MapBlock, detecting all air/non-air surfaces, collecting them to a list of faces

Render step

  • Create a list of MapBlocks in rendering range
  • foreach(MapBlocks in rendering range):
  • If it is not in front of the player, skip it
  • Draw the faces of the MapBlock

This is how Minetest works up to this day, except:

  • At some point, instead of storing faces, Minetest was made to store meshes.
  • In Minetest 0.3.1, occlusion culling was added to the render step
  • In Minetest 0.4.3, the list of MapBlocks to be rendered is cached, and sorted by texture

Hmm...