Thread safety and OpenGL
Threads are a huge part of KiwiCubed. When running an integrated server, you are already running 5 central threads at once (render/tick/network for the client, and tick/network for the server). And getting these threads to work together is rarely simple.
After I had first finished getting the server to send over player join packets, and getting player models to parse and render correctly, I figured I’d be done. However, whenever the client would try to render a new player, I’d get a weird OpenGL error saying that no vertex array object was bound.
This was weird, because I have a simple abstraction layer over OpenGL, that currently is very aggressive with making sure that no errors like this occur, forsaking a small amount of performance (because OpenGL is not what I want to spend my time debugging when implementing features). Because of this, it should have been basically impossible for the program to ever be in an invalid OpenGL state.
After adding some logs to see the exact ID of every vertex array object generated, saw that the ID for the player was always 0, and finally realized the problem.
When the client received a packet informing it of a new entity, the NetworkHandler would call a callback registered by a different part of the program. This callback would immediately spawn and setup the full entity. Crucially, for entities with EntityRenderableComponent‘s, this included generating OpenGL buffers and uploading data to them.
The thing is, LiteNetLib (the networking library I use) doesn’t run on the main thread. It uses some worker thread(s?) so it can send and receive packets asynchronously. This meant that, all entity setup code from network-received entities would be run on a background thread.
If you don’t know, OpenGL is very thread aware, and only lets you do operations from the thread where the OpenGL context is generated. You can manually set up a “shared context”, but the shared context actually doesn’t support specifically vertex array object creation, which makes it useless for my purposes anyways.
Basically, what would happen, is that the NetworkHandler would receive the entity and set it up on that thread, and then OpenGL would SILENTLY fail the several operations involved in setting up an entity.
Moral of the story: I hate threading. And also why does OpenGL not give errors for trying to use it from the wrong thread? wtf
(I used lazy initialization to fix it. Kind of a band-aid fix but it was the best I could think of at the time)