Devlog 3 - Multiplayer & A Fresh Start
Monday, July 25, 2022Hey everyone, welcome back to the devlog for my Minecraft-inspired game Delve! It’s been a while since my previous post – reason for that being that life has continually been getting in the way of me developing the game. (Having the flu wasn’t the way I was expecting to spend my July but oh well.) Anyway, since the last post, I’ve rewritten the entire game. I’m almost back to the point where I was previously, but with a better backend this time. And as a little bonus, I implemented some rudimentary multiplayer!
Early Rendering
For the rewrite I decided to change which OpenGL library I was using. So instead of handling window loading with GLFW, I’ve now switched to SFML .
Surprisingly, I didn’t actually make the switch for the rendering engine. I’ve still got to use basic OpenGL for my rendering, since SFML’s built-in rendering API is only available for 2D projects. After realizing this, I had to rewrite all my window generation code. Hey, it is what it is.
The reason why I switched to SFML is because it’s much more than just a graphics library. It has a great 2D graphics engine, sure, but it also comes with utilities for sound loading, vector math, and network code (this is the big one!). So it’s a really useful utility to have if you’re developing games in raw C++.
It also cut down on one more library, since it comes with its own image loading system which can be converted into an OpenGL-readable format. So no more stb_image necessary!
Now There Are Two of Them
Before I got a chunk system implemented back into the game, I wanted to play around with networking a bit. So I was able to spin up a little multiplayer test.
The way multiplayer works is actually pretty interesting. You’re able to establish a connection between the client and the server with something called a socket . Sockets basically work like gateways that you can send data on – you pump data into one end and it pops out on the other. Of course it’s a bit more complicated than that in execution, but that’s the gist of how it works.
To send meaningful data between the client and server, you need a consistent format for it. SFML provides a really safe and easy way to do that called packets . You’re able to package data into a packet and if you do it correctly, all the data will be safely transferred regardless of whether the client and the server have different architecture.
So the gist of my multiplayer code’s workflow is as follows: 1. Package client data (e.g. inputs from the player) into a ClientPacket 2. Send the ClientPacket to the server through a socket 3. Receive ClientPackets on the server and parse them back into client data 4. Update the game world with the client data 5. Package server data into a ServerPacket 6. Send the ServerPacket back to all clients 7. Receive the ServerPacket on each client and update what’s being rendered
Re-adding Blocks (after accidentally removing them)
A really weird issue I was having when trying to render other players was that whenever a player was being rendered, the block was not being rendered, and vice versa. As it turns out, I had implemented my vertex array attribute system wrong, so every one of my renderers was trying to read from the same set of vertices! A bit of fiddling around was necessary to get it working properly but in the end I was able to get things to function.
(By the way, you might’ve noticed some new art in the last screenshot there! That’s because this game is no longer a solo project – I’ve brought on my friend Adeline to help me out with art and game design. Expect more cool textures in the future.)
Heckin Chunkers
The last thing I did this time around was reimplement chunks! Since the new rendering engine uses element buffers I was able to vastly improve performance of the mesh-building. Right now the chunks are only implemented client-side – I’ll add them in server-side soon™.
While implementing this, I ran into an issue where some random blocks were missing in chunks. Curiously, when I decreased chunk size this issue was eliminated. As it turns out, this was caused by my chunks’ element buffers using a 16-bit unsigned int rather than a 32-bit one, so with enough blocks the element buffer was overflowing. I switched the element buffers to 32-bit and that fixed it right up.
I wanted to try a test to make sure the mesh-building algorithm worked with complex shapes. However, I didn’t have Perlin noise implemented, so I just used an “inverted cone” formula to generate the chunk. It worked as expected so I should be good to go forward with more complex worldgen in the future.
Conclusion
This post was a bit shorter than usual since I’m trying to keep things a bit more concise and less technical. Hopefully I succeeded on that front! I think next steps for the game are: - Add block removal and placement – kind of an important feature for a Minecraft-like! - Add server-side chunk architecture. This way both players can actually share the same world with each other! - Make the world infinite. This might be a bit of an extra technical challenge with the multiplayer but I don’t think it’ll be too difficult.
I’m currently in the process of applying for a bunch of jobs, so I’m not necessarily working on the game full-time per se, but I don’t have the flu anymore so hopefully progress should be a bit more steady going forward. I doubt you’ll have to wait as long for the next one. Hope you all enjoyed!