Sign in to follow this  
Kylotan

Writing robust and maintainable game clients

Recommended Posts

I've traditionally been a server programmer but recently have had to spend more time on the client side. While the networking side is usually somewhat simpler here, I've found the logic flow is made more complicated by the presence of user input and the GUI. The client is set up to receive messages asynchronously, but a player's flow through the GUI is quite linear and synchronous - they choose an option and expect that to lead directly to a response, even though the underlying code must keep rendering, polling for messages, and processing them accordingly. So I was wondering how people handle this in an effective way.

For example, imagine I have a menu with 3 options: Join Battle, Host Battle, View Leaderboard. Each button leads to a new screen with new GUI elements, but between clicking the button and showing that screen, you need to send off a message to the server (which is the easy part), and then wait for a response and transition to the new screen accordingly with the correct data in place. This means that while this menu is onscreen the game can be in one of 4 possible states (Waiting for button press, waiting for Join Battle response, waiting for Host Battle response, waiting for View Leaderboard response), and has to be expecting one of three responses from the server), which then have to trigger a state change and the appropriate GUI update with it. On a small scale this can be handled with a very trivial state machine, eg. an enum for the 4 states, but even then it gets unwieldy rather quickly. Some issues that come to mind:

[list][*]when a response comes in from the server, should we just stash it locally and tell the GUI to update its view based on the new data? What if the response is complex? (eg. it could be a payload of the requested data, or a failure condition with a diagnostic message.) It's not ideal to 'push' data to the GUI as that creates a dependency on your presentation layer. But it needs to be able to pull various responses, possibly more than 1 per request, and possibly some that can arrive at any time without being requested.[*]how do we handle unexpected or delayed responses? eg. If a user clicked 'View Leaderboard' and then clicked 'Cancel' before the data arrived, how is that best handled? Ideally the code that stashes the leaderboard info locally won't be inspecting the GUI to see if it's still interested, but there must be a mechanism in place for updating the GUI if it is.[*]if a state transition requires 2 or more different responses from the server first, what is a good way of waiting for all of them that won't involve an explosion of sub-states? (Assuming a reliable connection.)[*]how do you handle a mixture of explicit state and implicit state? (eg. explicit might be the player clicking GUI buttons to move to a different menu, but implicit might be whether the connection is up, whether you're authenticated, etc.) Is there something better than having to check implicit state repeatedly across all the explicit states?[/list]
I have a vague feeling that much of this is a good candidate for a publish/subscribe system - when you send off a request, you'd subscribe to the expected responses, and unsubscribe when the responses come in or when you elect to cancel. However I'm still not clear on how I'd adequately handle error conditions in that situation (as you don't want to have to explicitly subscribe to individual error responses), or on how to synchronise the game logic with the GUI since the GUI needs to wait for the game logic to handle the response before it can poll for fresh data.

Given that I can't find anything in the Massively Multiplayer Game Development books about this sort of thing, I'm guessing this is either a very difficult problem or a very trivial one! I hope it's the latter and that I'm just missing the obvious, so hopefully someone has some suggestions?

Share this post


Link to post
Share on other sites
[quote name='Kylotan' timestamp='1318870884' post='4873530']
For example, imagine I have a menu with 3 options: Join Battle, Host Battle, View Leaderboard. Each button leads to a new screen with new GUI elements, but between clicking the button and showing that screen, you need to send off a message to the server (which is the easy part), and then wait for a response and transition to the new screen accordingly with the correct data in place.
[/quote]

I would recommend against this. Instead, build three screens, where each screen knows what messaging it wants. Build it as a state machine. Have a screen always able to render itself based on its current data. Have a "model" of the data that is updated by the networking. If you're doing a PC game, you render the screen 60 times a second anyway, so you don't need any notifications; when data changes, the screen will magically update anyway.


Everything needs to be event driven, and nothing should synchronously wait for anything else.

The state machine for "join a game" consists of sending a packet to the server to list available games when you first enter the screen, and every X seconds thereafter. Further, the state consists of "list of games" and "selected game id." The user manipulates "selected game id" by doing down/up/clicking in the displayed list; the network manipulates the list by sending updates. When the user clicks "join game," pass the "selected game id" forward to the "in game" screen that you switch to.

The only difference if you're not doing a PC game is that you introduce a "data changed" callback from the data model, and have the game list presenter request a redraw in response to that event.

Share this post


Link to post
Share on other sites
Writing a clean, usable front-end is a complex task, which is traditionally underestimated.
There aren't as many states as you think. It's not the number of states that makes it complex, but as you pointed out the fact that the user expects things to be linear and synchronous.
Waiting for Join Battle, Host Battle, View Leaderboard are essential a single state from the GUI interaction point of view: the state of waiting for something.
While in wait state you usually display a modal dialog box and do not allow the user to do anything else. Exactly what you are waiting for is just a modifier.
Ideally everything would be asynchronous like hplus0603 suggests. But that is not always possible.
Let's imagine a turn based mafia game where you press a button to attack another user. Assuming that the fight is simulated immediately, it doesn't make any sense allowing the user to continue to interact with the GUI until you know the result of the fight. This is because the user needs to know how many of his goons were killed, how much cash he looted, before he decides to attack again or perform other action.
So you display a modal dialog box. In a non ajax browser front-end the equivalent of this dialog box is waiting for the page to reload.
Attack is a synchronous event. An example of asynchronous event is a chat message. These can happen in the background, because they are not critical to decision making.

Share this post


Link to post
Share on other sites
[quote name='hplus0603' timestamp='1318882622' post='4873610']
Instead, build three screens, where each screen knows what messaging it wants. Build it as a state machine. [/quote]

When you say build it as a state machine, are you considering each screen to be one discrete state?

If I wait before switching to the new screen before having the screen send off the request for the data it needs, that does make things much easier to manage. The downside is that I can't use the trick of continuing to render the old screen while I'm waiting for the data (to hide the latency a little) but I don't suppose a 200ms loading message is the end of the world!

I guess I've been blinded by the fact that object-based state machines are usually overkill for most purposes, but here is one situation where it would be a lot cleaner than the switch-style alternative. I expect I'll give it a go, anyway.

[quote]Have a screen always able to render itself based on its current data. Have a "model" of the data that is updated by the networking. If you're doing a PC game, you render the screen 60 times a second anyway, so you don't need any notifications; when data changes, the screen will magically update anyway.[/quote]
It's not always that simple because if you use a GUI toolkit then your GUI has intrinsic state which you need to update. Sure, you can re-read the data from your model into the GUI every frame, but you're probably not going to keep getting 60 frames a second if you throw away your leaderboard data and rebuild it every time through the update loop. But I can probably optimise that with an observer-style pattern - each screen observes the data, and when the data changes, the screen can re-read what it needs. I guess this is what you mean with the data changed callback you mention.

[quote name='hiigara' timestamp='1318899459' post='4873702']
While in wait state you usually display a modal dialog box and do not allow the user to do anything else. Exactly what you are waiting for is just a modifier.
[/quote]
Yeah, the problem is that the code that handles the dialog box (or equivalent - I don't have access to a dialog box, and certainly not a modal one, but I can improvise) doesn't receive any data directly. So the issue is how the code showing the waiting screen knows when the appropriate response has arrived, and what to do then. GUI boxes don't watch the network directly, and nor does the logic that produces the dialog box in the first place, so it's quite a decoupled system - the challenge seems to be in knowing how to couple it!

Share this post


Link to post
Share on other sites
[quote name='Kylotan' timestamp='1318961585' post='4873987']
I guess I've been blinded by the fact that object-based state machines are usually overkill for most purposes, but here is one situation where it would be a lot cleaner than the switch-style alternative. I expect I'll give it a go, anyway.
[/quote]

Actually, I would expect each screen to be a state machine, and then there to be a higher-level state machine to decide which screen to display.
Note that just animating from one screen to the next (using zooming, twisting, fading, or whatever) is likely to take 500 milliseconds, so you can hide whatever you want there.


[quote]It's not always that simple because if you use a GUI toolkit then your GUI has intrinsic state which you need to update.
[/quote]

In that case, you want to use invalidation from your model. When the model changes, it sends invalidation signals which you can listen for, pick up in the main loop, and edit the stateful GUI however it's needed. If you're doing a GUI on top of OpenGL or D3D, though, I would highly recommend against a stateful GUI like typical windowing toolkits; instead generate your GUI graphics each frame; it's much more responsive and works great with a typical 3D game loop/renderer.

[quote][quote name='hiigara' timestamp='1318899459' post='4873702']
While in wait state you usually display a modal dialog box and do not allow the user to do anything else. Exactly what you are waiting for is just a modifier.
[/quote]
Yeah, the problem is that the code that handles the dialog box (or equivalent - I don't have access to a dialog box, and certainly not a modal one, but I can improvise) doesn't receive any data directly. So the issue is how the code showing the waiting screen knows when the appropriate response has arrived, and what to do then. GUI boxes don't watch the network directly, and nor does the logic that produces the dialog box in the first place, so it's quite a decoupled system - the challenge seems to be in knowing how to couple it!
[/quote]

I don't like modal dialog boxes. It's fine to show a progress spinner in the middle of the window, and dimming most options, but some option (escape/red/whatever) to escape back to the main screen and cancel whatever you're currently doing should always be available.

Share this post


Link to post
Share on other sites
By the way, in the dialog box you can put whatever you like: an animation, a progress bar and the cancel button.
Although, in my opinion, if a request requires a progress bar, that means it will take more than 10s to finish.
Typically that request would not be synchronous in a game, because you have to give the player something to do in the mean time.
He will not like to wait more than 10s without doing jack.

Share this post


Link to post
Share on other sites
I'll have a go at making each menu a separate object, and within each menu will be a simple state machine, probably just with 2 states (1. waiting for data, 2. displaying data and waiting for input). The initialisation phase of each menu object will send off the request for the relevant data, and will also subscribe to update/invalidate calls from the model, which will be made when the reply or replies arrive.
[quote name='hplus0603' timestamp='1318995382' post='4874134']

if you're doing a GUI on top of OpenGL or D3D, though, I would highly recommend against a stateful GUI like typical windowing toolkits; instead generate your GUI graphics each frame; it's much more responsive and works great with a typical 3D game loop/renderer.[/quote]
Personally I find it to be a massive inconvenience! I like to be able to read data from the model once, translate or format it for the display, stick it into the GUI object, and then forget about it. My experience with the 'immediate mode' GUI systems, eg. Unity3d's GUI, is that managing all the state (eg. scrollbar locations, window positions, the current text in edit boxes, which items are selected within a list) is a complete nightmare. It's probably fine for FPS games and other stuff with very little GUI state, but for more complex stuff like RPGs I find it a hassle. Still, this is possibly a bit off-topic!

Share this post


Link to post
Share on other sites
[quote name='Kylotan' timestamp='1319030081' post='4874315']
I like to be able to read data from the model once, translate or format it for the display, stick it into the GUI object, and then forget about it. My experience with the 'immediate mode' GUI systems, eg. Unity3d's GUI, is that managing all the state (eg. scrollbar locations, window positions, the current text in edit boxes, which items are selected within a list) is a complete nightmare.
[/quote]

At that point, you're comparing a complete GUI widget system with a system where you implement the widgets themselves. If the widgets are already implemented, there wouldn't be much of a difference. If you have data binding, just create a data item for "position of scroll bar" and bind your direct-mode widget to that value.

However, how you generate the bits on the screen are kind-of far from the charter of this particular group :-) Although the subject of data binding and invalidation is actually quite on-topic; the source of data changes is just different!

Share this post


Link to post
Share on other sites
I always have a gray "message box" come up between transitions that say what it's doing.

They click "View Leaderboard". That might take a couple seconds to send the msg to the server and get the response then process the response then show the results. So when they click the button have this "message box" be top most and darken the stuff behind it. Send off the msg to the server and wait for the response. When you get the response you send it to the Leaderboard screen to process and fill in the gui. When it's done doing that you remove the "message box" and show the Leaderboard.

Share this post


Link to post
Share on other sites
[quote name='rpiller' timestamp='1319221215' post='4875107']
I always have a gray "message box" come up between transitions that say what it's doing.

They click "View Leaderboard". That might take a couple seconds to send the msg to the server and get the response then process the response then show the results. So when they click the button have this "message box" be top most and darken the stuff behind it. Send off the msg to the server and wait for the response. When you get the response you send it to the Leaderboard screen to process and fill in the gui. When it's done doing that you remove the "message box" and show the Leaderboard.
[/quote]

A better UI would be to immediately start sliding the screen from the "menu" screen to the "leaderboard" screen. The "leaderboard" is currently empty, thus in the middle of the "list" area, you'll see a spinning "updating" indicator. When entries come in, that indicator goes away and real entries are slid in. Smooth animation can also hide a lot of latency, so chances are that the leaderboard screen will begin populating while it's still being animated in.
To implement such an animated interface, you need to separate a number of different entries in your program (screen display, widgets, data storage, data fetch) and you must build everything as an asynchronous, state-based, invalidation-driven, continuously-refreshing system. You should never think that you are "waiting" for anything to happen; instead you are in a particular state (say "displaying leaderboard with 0 entries") and when you switch state (say, "displaying leaderboard with >0 entries") then that should be a smooth transition.
Will it work to build UI the old, modal, way? Yes. But any user of an iPhone or Xbox will think your system is old-fashioned. The bar has been raised, and it's not coming down again :-)

Share this post


Link to post
Share on other sites
[quote]
The bar has been raised, and it's not coming down again :-)
[/quote]

I guess I personally don't care as long as it doesn't take long. Just thinking of counter strike which millions of people still play. When you connect to a server they give you the same kind of setup I was talking about but add a progress bar and status updates as the client gets responses from the server. I think it's a cool feature to have what you are talking about, but not a must for me as long as I'm getting some kind of update and the game is fun! I've never said "weeeeee" when navigating through menu systems :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this