Sunday, 18 January 2015

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?
Your method will nearly always be provided with a World object, or an object containing a World field (typically called worldObj).  If so:
  • if World.isRemote is true, this is Client side
  • if World.isRemote is false, this is Server side.
As a last resort:
  • FMLcommonHandler.instance().getEffectiveSide() returns Side.SERVER or Side.CLIENT.  However it is not robust in all situations so you should be wary.
When the client and the server need to synchronise with each other, they exchange information over the network.    Even when a single player game is running, the client and server are still completely separate and do not access each other's objects.  If your code running on (say) the server side accesses objects belonging to the client side, it will lead to random crashes and strange behaviour.  It will also cause your code to crash immediately when your mod is installed on a dedicated server.

3 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Thank you for your blog, it's very useful for developers. What you wrote about FMLcommonHandler.instance().getEffectiveSide() is "not robust in all situations" has helped me solve some obscure issues with our mod.
    See https://github.com/micdoodle8/Galacticraft/issues/1419. I guess I didn't read the comments on getEffectiveSide() before... seems that it will return the wrong result on servers if there's any multithreaded code, because only the main Server thread will give the correct result. Also in practice a server which is saving and shutting down seems to return the wrong result sometimes.

    As a coder, the main situation I have problems with is the validate() code for tile entities. Since Minecraft version 1.6.4 (I think), the vanilla Chunk code which places tile entities in the world and validates them does not set the TileEntity.worldObj until after the TileEntity has been validated. It means that a tile's validate() code cannot know which side it is on without calling FMLCommonHandler.getEffectiveSide() or some other trickery.

    ReplyDelete
  3. Keen thanks for that, glad you find it useful :)
    The multithreading is going to cause plenty of headaches I think - network code and chunk rendering in particular. I'm tiptoeing through the minefield still myself.

    I had a quick look and I think in 1.8 the world is now set before validate?

    public void addTileEntity(BlockPos pos, TileEntity tileEntityIn)
    {
    tileEntityIn.setWorldObj(this.worldObj);
    tileEntityIn.setPos(pos);

    if (this.getBlock(pos).hasTileEntity(getBlock(pos).getStateFromMeta(this.getBlockMetadata(pos))))
    {
    if (this.chunkTileEntityMap.containsKey(pos))
    {
    ((TileEntity)this.chunkTileEntityMap.get(pos)).invalidate();
    }

    tileEntityIn.validate();
    this.chunkTileEntityMap.put(pos, tileEntityIn);
    }
    }

    From what I've found so far, vanilla uses something a bit different to tell which thread it is on - see the (confusingly named) MinecraftServer.isCallingFromMinecraftThread()
    public boolean isCallingFromMinecraftThread()
    {
    return Thread.currentThread() == this.serverThread;
    }
    Something like that might be applicable if you put it in your proxy and check both Minecraft and MinecraftServer as appropriate.

    Cheers
    TGG

    PS Galacticraft is rather awesome I have to say, I've had a lot of fun with it!

    ReplyDelete