Until today, Flick Karts’ clients had parameters such as the number of laps, or even the circuit to load, set to hard-coded values. This works well at the earlier stages of server and client development, but testing how the game handles, for instance, the winning condition under different number of laps was starting to become cumbersome: We had change code, recompile it, and then test again for all conditions. And given that players will have the option to change these values prior the beginning of each race, it made sense to have already a primitive lobby screen where we can change these values at runtime.
This is what we got so far:
You may notice that both screens are exactly the same, and this was by design: We want to make sure that clients receive correctly the settings that the player hosting the server changes. Also, although i hope it will look way prettier, the final game’s version is going to work the same, so that other players can see, and maybe discuss with the host, what to expect when the race starts.
What might surprise you is that it took us around ten hours just to sandwich this lobby between the menu and the game play screens.
Evidently, it was not due to the extreme care we took to build this part just right;
we spent, at most, an hour for the layout of widgets, in part thanks to the ease of use of libGDX’s scene2d.ui
package.
No, things got complicated due to how easy is to get confused when working with networks.
Networking code is naturally asynchronous: It would not do to stop rendering the game while the client receives data from the server and, vice-versa, sends data when the player makes her move. That means heavy use of threads that run in background and somehow notify the application when anything of interest happens, be it the start of a new race or that someone quitted in anger. That “somehow notify” is traditionally implemented via callback functions, that Java calls listeners and are just an object-oriented version of the observer pattern.
Listeners are pervasive in libGDX to the extend that not only it has them for the “usual aspects”, like input, but the whole game is in fact a big listener that receives events from the back-end to manage the application’s life-cycle. Following suit, we also defined listeners for all our game’s events and soon found out the hard way that if not done carefully it is too easy to end up in the so-called callback hell.
Just to give you an example, this week we doubled the number of listeners and now have 20 methods that we have to know about when sending and received messages between server and clients. As you might expect, discussions that begin with “who does call this method and when, again?” are becoming more frequent than we feel comfortable with.
We have not yet found a viable alternative for it, though.