Create a server class (to handle connections and messaging) and a client class (to handle connecting to server by IP and port).
Then ask the player whether they want to create or join a server.
- If create, start a server and join it with the client.
- If join, join the existing server.
Minecraft singleplayer can be opened to the LAN, by just clicking that option. So it is already running singleplayer with server client setup. It works, because latency is basically 0. It adds a bit overhead, but nothing noticeable if done decently.
You should definitely run the server in its own thread.
Handle all actions as network messages. Instead of directly calling
WalkForward() (for example), you would do:
and in the server
void Handle(MessageBase message)
if(message.readByte() == InputMessageType
The server would store the world state with all objects in the world, their locations, rotations etc. It would calculate each client's local area for objects that they should know, and send each a personal
worldStateMessage (or similar).
Each client would also have world state, but from its own view. It would not know about all the objects around it, only those that server has sent it. Clients constantly receive
worldStateMessages, maybe something like 20 times per second?
Why this works
With this kind of system, you would not send "DestroyBlockXY", but "Client X targeted xyz and clicked mouse 1". When the server receives such a message, it would execute that action in its own world model and notify other clients, that "Client X just targeted xyz and clicked mouse 1". Other clients would pick up this message (if it's in their local area) and update their view of the world, to mirror the server's.
As you see, you can't just plug a client-server model in. You have to code your game around it. But when done like this, you can play on a local server or internet server, using exactly the same code.