Terrain Sync
In the previous post, I talked about the world editor, and how it can export usable world files now. In this post, I'll talk about work that was done to get the Server loading up those world files and sending terrain data to Clients.
First, here's a quick overview of what's been done:
- Client: Integrated terrain library
- Client: Refactored movement code to allow traversal of sloped surfaces
- Server: Renamed Giant Rat NPC to Chonkrat
- Server: Implemented world file loading
- Server: Implemented actual ctrl-c handling for a graceful shutdown
- Server: Bugfix: Fixed crash when two players picked up an item on the same tick
- Client & Server: Implemented terrain syncing
I added two new Server packets to the game protocol: The WorldInfoPacket, and WorldTerrainDataPacket.
Server - 0x16 - WorldInfoPacket
size: u32 (4 bytes)
chunk_size: u32 (4 bytes)
The WorldInfoPacket is sent after a client connects, and informs the client about the terrain data they are about to start receiving. This is so that the client can get the world ready, essentially creating a properly sized terrain with "empty" chunks that are waiting for heightmap data. For now, the only important information is the world size (in chunks) and the chunk size (in tiles). I'll add more to this packet as time goes on, such as a value for sea level, lighting values, or maybe info about the time of day.
Server - 0x17 - WorldTerrainDataPacket
chunks_len: ulong (8 bytes)
chunks: list (? bytes)
chunk_x: u32 (4 bytes)
chunk_y: u32 (4 bytes)
tiles_len: ulong (8 bytes)
tiles: list (? bytes)
bl: byte (1 byte)
br: byte (1 byte)
tl: byte (1 byte)
tr: byte (1 byte)
For now, the WorldTerrainDataPacket is sent directly after the WorldInfoPacket. Since the world is so tiny at the moment, all chunks are sent. Tiles are still just defined by the heights of their four corners. When I add new tile types and other stuff I outlined in the previous post, this will change a bit. Eventually, the server will only send these whenever a player crosses chunk borders, and a new area of the world needs to be streamed. Although, lately I have been thinking about it, and I may end up going with a more front-loaded approach. The server would send all terrain data when a client connects, expecting them to cache it. A Server-side world version would be maintained, and when a client with a null or outdated world version joins, they would be sent the latest data. Of course, all of this could just be shipped with the client itself, but then I lose the ability to modify the game world instantly for all players of the game - and I think that's really cool.
Above is a screenshot from the editor, where I'm placing a few coin spawners.
Note that the only thing clients care about from the world is the terrain. Entities spawned at runtime are still synced in the usual way. The entity placements within the world file are just read when the Server starts, and the referenced entities are instantiated. I haven't implemented pathfinding or "walkable" tiles yet, so NPCs still wander out into the sea and will continue wandering until they reach the void.
This post concludes my work for October. It was a productive month! I am thrilled that I actually hit my stretch goal, which was to get the terrain system implemented (in some form) on both the Client and Server. I'm guessing the next two months won't be as exciting, due to the holidays. As I continue development, I think I'll be focusing on these areas:
- Better item drop functionality
- Better skill exp gain and level ups
Thanks for reading.