utorak, 13. rujna 2016.

New game loop

I had fun time learn how operating systems do (and don't) handle timing, what's guarantied, what's not and how and how achieve results by messing whole OS. On the one hand I'm disappointed there is no guarantee for having timing resolution below 64 Hz (ticks per second) and that there is no cross platform way of getting the resolution. But on the other hand I've observed 1000 Hz resolution on all machines I have access to.

Let's back off a little bit. Game loop is part of game logic which juggles when input events (mouse, keyboard, ...) are collected and processed and when a image frames are drawn. Typically it repeats following steps: check stop condition, process input, update game state, draw frame, sleep until next frame is needed. In previous Stareater version there was no explicit game loop, it was part of WinForm's internal "message pump" which in turn is too an endless loop that waits for a message, delegates it to an appropriate handler and checks if it got "quit" message. For signaling when to draw a next frame there was a timer component which was periodically sending "timer tick" messages. It was serviceable but not a "proper" solution. Timer's timing and my implementation of it's handler were sort of a "do it when you can".

When I started adding more game loop features, namely adjustable FPS limit, a mode without FPS limit and "battery mode" where game loop is bit more mindful about power consumption, it turned out I have to do a bit of research and rework the stuff. When it comes to timing precision there are two options, busy spin (a loop which repeatedly checks the clock) and thread sleep which is more concerned with letting other threads breath then timing. Timer is basically same qualitatively as thread sleep with a difference in how a thread is notified to wake up. So it's not surprising that most game engines, including the one supplied with OpenTK (graphics library used in the project), use busy spin approach. Busy spin is good when you have a lot of animations and movement in general but one has to keep in mind that such method uses all available CPU time which drains more power, heats CPU and makes cooler fans spin while not doing constructive work during the wait period. Since Stareater may or may not have a lot of animation, I made support for use both timing methods.

There was even bigger change to game loop, it was moved to a separate thread. I looked around the internet what is the best way to implement the game loop and there was no clear best practice. One approach (repeated from basic game dev tutorials) was to have ordinary loop which has extra step in cycle to let message pump process accumulated events. Another approach was vague reference to rendering in background thread. I've decided to marry those two concepts and suffer the consequences. The biggest reason I did that is to separate WinForms stuff from OpenGL stuff. This way window can handle it self at it's own pace and frames can be rendered in parallel. No need to send redraw request through who knows how many layers of bureaucracy. Many programmers fear the multithreading but it turned out to not be that bad. Sure I had my share of deadlocks (threads blocking each other) and race conditions (parallel tasks messing with eachothers data) and had to learn some new stuff but it was worth it. I finally had a chance to use thread join operation and discovered wonders of atomic operations and it didn't take long to fix bugs. In fact I'd like to share a few stories:

  • First one was an experiment. In theory when a process quits all of it's threads are killed so I tried to make game loop without explicit end condition. It turned out that exiting an application GUI left background thread working alone. Maybe it was the case when running from Visual Studio or Sharpdevelop, I didn't investigate further because I needed stop condition anyway. Game loop had to have some cleanup logic and it has to stop before GUI components get cleaned up.
  • What happens when GUI and main thread hosting it quit before game loop thread? Errors and exceptions because OpenGL is being used after the application had it deinitialized (disposed the context). Cure for that bug was use thread join so that main thread waits for background thread to finish.
  • More code you have guarded by synchronization mechanisms means deadlocks are more likely. When a combat happens, main thread informs game loop to switch to space battle "scene". Game loop switches scenes and calls scene initialization inside a guarded block and battle scene initialization "invokes" a piece of code on main thread. This code hides drop-down menus which triggers resize of draw area. Main thread tries to inform game loop of resize event but has to wait because loop is in guarded section so no thread can advance. Fix for this one was to make lighter signalization mechanism. Instead of guarding whole blocks of code I've reduced guards to only cover receiving signals (not handling) and simplified signals themselves (used atomic test and set instead of "monitor"). In retrospect asynchronous invocation would fix an issue too but simplifying code has further reaching benefits.


Enough with technical stuff, here is how settings menu looks like now. You can select or enter frame rate frequency limit or choose unlimited mode which will draw frames as fast as possible. Options below regulate whether to use precise but power consuming timing method or potentially imprecise but much less wasteful method. A little technical detail, power mode detection (whether a computer runs on battery or is plugged in) doesn't work in Mono so Linux and Mac won't have a benefit of game automatically switching modes. Players on those platforms will have to use "always" and "never" options which force the usage a particular method.

This is just a first phase of reworking graphics engine. It went better then expected. I know I'm repeating for the hundredth time but multithreading issues are nasty beast and I'm glad there wasn't so many of them. Next phase is moving to newer OpenGL API and improving performance. I've run profiler and noticed there are very bad performance issues. There is a lot of work left to be done.

subota, 3. rujna 2016.

Plan for 0.6 features

Loads of tech, that's the plan! And some space monsters.

It's been only a month and I already feel like v0.5 rewrite was done over a year ago. Strictly speaking software is never finished but I drew the line back then. Big rewrite was over and there were no glaring bugs. There was some functionality like ship design upgrade and colony/star list which didn't make through migration but they are either moved to future plan or moved back to drawing board.

This version will be about improving the "engine" and adding more gameplay, both featruewise and contentwise. There will be again moderately big code refactoring but I hope nowhere near as much as in previous version. In any case it should be possible to break it down to smaller (doable within a month) chunks. Code improvements made in v0.5 rewrite made that possible. Without further ado here are features planned for version 0.6:
  • Diplomacy with some basic capabilities like "declare war" and "cease fire". Will see for more complex interactions but for now I'd focus on laying foundation, figuring out where to put negotiation logic and how to handle it on user interface.
  • Hot seat multiplayer. Multiplayer as a whole is not in short term plan but I made a statement some time ago about how certain change brought the hot seat implementation closer to reality. It's time to test the theory.
  • Semi-exclusive research like research system in Master of Orion II where you can get only one item per technology field level. Stareater won't be so harsh, you will be able to get all items but you'll have to spend extra development effort. Idea is to instead of picking only one item you'd make your first, second and third choice. First choice item will be the cheapest to prototype, second choice will be expensive and the third will be even more expensive. On top of reworking research system I plan to start adding actual technologies to the game. I've made an draft document with about 120 candidates and more are to be added.
  • Ship repair and upgrade will be reintroduced. Ship damage will persist after battle and colonies will passively produce repair points. Once all ships at a star are repaired, surplus repair points will be used for upgrading or retrofitting ships.
  • Missiles in combat. First ambition level is to have "instant hit" missiles which will function similar to beam attacks but with slightly different chance to hit and limited ammunition. Next ambition level are torpedoes, missile which move across the map like space ships, can be destroyed and do area damage.
  • Space monsters are going to remind you that you are inside of a living organism. Most of them won't attack you directly but will interfere with growing your empire. On the development side their purpose is to give you something to fight before meeting other players or during the peace time and to provide some lore about the Stareater universe.
  • New rendering engine is already in development. Previous engine was using old OpenGL API, rendering loop was not very controllable when it came to timing and I'd like to make it easier to link visible objects with user interaction. Part of it is already done by now since messing with OpenGL and multithreading was more interesting to me then writing a post so that's why there is a delay :).
  • OpenGL based GUI is something I was looking forward to for a long time. WinForms are so restrictive, even for making ugly but servicable UI. The game uses a lot of lists, each displaying items differently and WinForms have two kind of GUI controls for lists: a simple ListBox where each item is just text and ListView which is basically carbon copy of file list in Windows Explorer. Both are inadequate for my needs so I've been faking lists with other components and the process is so prone to errors and full of workarounds. On top of that there is no way to easily change the look of controls so if I'm going to basically rewrite their drawing logic I might as well do it in OpenGL and solve other problems.
  • Refactoring state data is something I've been constantly doing in the last version. State data are stored in data types which beyond just holding the data don't have any more functionality of their own. The problem is there are a lot of such data types and each needs copying, saving and loading logic so there is a lot of repetitious code which looks like it can be generalized except no programming language feature can do that. Efficiently at least. In the last version I've tried C# text templates which are pieces of code for generating code (or any other kind of text file). They work but are hard to debug and update. I've been prototyping another approach, looks promising so I'll try it out.