Devlog 1: BS-Odin
Published: 2026-06-19
Multiplayer setup done! Server and client work flawlessly!
I spent a night and a morning to write this devlog, I am spent…and no AI wasn’t at all used to write this up
Context
This devlog is going to be a bit bigger than my normal work, because I started this project not knowing about Stardance. So here we go.
I started this game meaning to follow Casey Muratori’s Handmade Hero playlist. But I that is another project now and this has branched from very initial work from that project. I used Odin-lang for this project because I wanted to have fun and I never had used this language before!
First Day
#86f274e — these are the commits for this day
Now the first day, I already had a working window. I was using SDL library for window opening…but it had a problem, over all my desktop environments the opening of the window caused a lot of jitter and glitch effect…I googled a bit and found out that a simple flag fixes it! SDL stops the compositer running for performance reasons, which caused the problems.
sdl3.SetHint(sdl3.HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0") Day 2
This is my first time doing any networking, so I do not know where to go, neither do I know how to start.
I spent the whole day making everything from Window management, networking, rendering and some state management. I decided that I will have different directories for both the client and server. But will have a common dir for the common data that will be passed b/w the two.
A lot of work was done today, a lot of googling and a lot of copy-pasting. The end result was surprisingly very satisfactory!
The enet library is very easy to use. But I had one problem that I unable to overcome - identifying and differentiating the different packets sent over the network. I had to ask AI for some help in this regarding. The answer was very simple! I just have a single enum in the top of the packet and check it to match my required one!
PacketType :: enum u8 {
NEW_JOIN,
PLAYER_INPUT,
SERVER_OUTPUT,
}
PlayerInput :: struct {
type: PacketType, // this is always PacketType.PLAYER_INPUT
x_axis: f32,
y_axis: f32,
}
ServerOutput :: struct {
type: PacketType, // this is always PacketType.SERVER_OUTPUT
player_count: u8,
states: [MAX_PLAYERS]PlayerState,
} Day 3
Today I implemented text drawing, it was really simple with SDL_ttf3, but still a tedious job to do. Not fun. btw, I decided to use Supercell’s font cause I like that font!
I made different states of screens to! This is just a placeholder for something great! Right now for each state it shows different colours and some text centered. I also have a permanent fps shower in the top left of the screen.
Currently the frame is capped at 60fps but the fps text fluctuates a lot. I have to deal with this later!
I also took some time to make the code more modular. I like to keep things separate.
Day 4
Today was a light day, nothing special, I just fixed the fluctuation of the FPS using exponential moving avg. While researching for this I also found out about cumulative moving average with which we can calculate avg without needing to store any of the previous data. We only need the new data and the calculated avg from the previous frame! (This will definitely come in handy some day)
In the mean time(pun intended), I also setup a frame time below the fps, because frame time is much more easier metric to measure performance of a program than fps is.
Day 5
Today was also a peaceful day I implemented Tracy support for my game! This is very crutial for performance. Odin didn’t have official bindings for this library. So I forked one un-official bindings that wasn’t updated for more than 2 years iirc and made it work with the latest Tracy!
Day 6
Today I spent most of the day trying to optimize the text generation and rendering. I saved a total of 2 micro seconds! I’ve made a blog discussing this more.
Day 7
Up until now I was using different scripts (hmh.sh and hmh_server.sh) for building the client and server. Today I decided to change that because editing both the scripts individually was very tiring and error prone. I decided to merge the two scripts into build.sh and build server or client based on the arguments passed.
I also spent some time abstracting away the networking library (ENet) so that I can always swap with other networking library if I needed to and I think this was the most stupid and useless thing I ever did in this project, it was really not required.
Day 8
Today I was monitoring the ram and cpu usage of my server. I found that when I run the server the ram usage got up by 400-500mb ram! I was devastated! But that wasn’t my server that was using ram!
It was the odin compiler that was holding my ram hostage! I asked in the Odin discord if this is a bug, they kindly explained that odin compiler does that so that when a programme exits successfully it deletes the binary created!
So now I no longer use odin run command in my build script. Rather I build the binary usign odin build and run the binary from the script itself. That way my server only uses 1-2mb ram :)
This is great! I slept happy today.
Day 9
I spent the whole day trying to connect my server over the internet using Hackclub’s #nest facilities! But alas! I still do not have the ability to do that.
Apart from that I made a config.ini file that currently has the host and port data. Later it will have more stuff. I also put all my state inside of a global struct named global.
GlobalState :: struct {
quit: bool,
net: Network,
time: Time,
input: common.PlayerInput,
client_state: ClientState,
render_state: common.ServerOutput,
}
Time :: struct {
fps: f64,
frame_time: f64,
dt: f64,
show_fps: bool,
countdown: common.CountDownOutput,
}
Network :: struct {
port: u16,
host: cstring,
}
global: GlobalState = {}
For future me
I really need to stop working on these useless things and actually work on the project. Yes! from tmrw I will lock in!
I will add mouse input and use that to rotate a rectangle/sprite around the player, like the player is holding a stick or smth…