Monday, 7 October 2013

The Client <--> Server division

The Minecraft code can be divided into two “sides” – Client and Server.
  • The Server side is responsible for maintaining the master copy of the world - updating the blocks and entities based on packets received from the client, and sending updated information to all the clients.
  • The Client side is primarily responsible for reading input from the player and for rendering the screen.
There is one server, and a number of clients which connect to it. Even in single player mode, the server and client code are running simultaneously (in separate threads).

Some parts of the code are used by both Client and Server code, for example Block methods.  If you are writing code which might be called by either Client or Server, how can you tell which side has called the code?
First preference: if your method has been passed a World object, or the Class you are extending has a World field (typically called worldObj):
  • if World.isRemote is true, this is Client side
  • if World.isRemote is false, this is Server side.
If your code has been passed a NetHandler:
  • NetHandler.isServerHandler() returns true if Server side.
As a last resort:
  • FMLcommonHandler.instance().getEffectiveSide() returns Side.SERVER or Side.CLIENT.

Client and Server run in parallel

Communication between the Client and Server sides takes place through Packets, which are sent back and forth using NetClientHandler and NetServerHandler.  The basic steps are shown in the diagram below.  There are a large number of different packets used:
* Packets from Server to Client

Some guidelines on the Client<-->Server communication here.

Communication from Client to Server

Communication from Server to Client

If you need to send packets in your mod code, Forge has thoughtfully provided PacketDispatcher to send packets to the client or the server without having to retrieve a NetServerHandler or NetClientHandler.
·          .sendPacketToServer()
·          .sendPacketToPlayer()
·          .sendPacketToAllAround()
·          .sendPacketToAllInDimension()
·          .sendPacketToAllPlayers()

If the existing packets don’t meet your needs, you can create a custom packet Packet250CustomPayload.  More details here.

In many cases, you won’t need to create custom packets to keep your client and server code synchronised.  The vanilla code and Forge code do this in a number of ways already:
  • Creation, Removal, Movement, health, actions etc for Entities.  This is accomplished for new Entities by Forge packet EntitySpawnPacket.  You can define a custom handler for this using EntityRegistration.setCustomSpawning().
  • Removal or placement of Blocks
  • Adding DataWatcher variables to your Entity (see here)
  • Containers (Inventory, furnace, etc)