Skip to main content
gamedev.net
🔒 Locked

Handling Exceptions

Started by jmcmorris 3 replies 1.7k views
Original Post
jmcmorris
jmcmorris
What is the best approach for handling exceptions when parsing a received message on the client. Such as the data mentions a character the client has no idea about for whatever reason. I am working with a TCP connection so I don't have to worry about out of order or incomplete messages. Should I log the exception and then send a message to the server notifying it that the client had problems parsing the message. Following the example, the client would send the server "Who is character X?" and then the server would reply back with the response. Is this overkill considering this will very rarely happen - only when there is a bug somewhere? Then again, what would the client do if it has problems parsing this second message.

Unfortunately doing nothing would leave the client in a unhappy and confused state. Would closing the game gracefully and asking the user to upload their logs be a better approach?

As you can see, I am uncertain what to do in this situation. If anyone can provide some insight that would be greatly appreciated.
Drew_Benton
Drew_Benton
Implementing an error reporting mechanism is very useful and strongly recommended as it can greatly reduce the debugging time required to determine why the bug is happening. You need more than just the packet though, since the client state used for parsing it is relevant. For example, a valid packet might crash the client only because the client state got corrupted from something else, say a user trying to cheat. In those cases, the problem isn't your fault, per se, so you don't want to waste a ton of time trying to find a server error that is not really there.

You do want the client to exit gracefully as possible. You don't want the game to leak a massive amount of resources due to a crash if you can help it. If the exception is non-recoverable or if it just doesn't make sense to try and recover from the error, exiting is fine. A lot of games I've seen just crash out and have their error reporting program run afterward (since it was registered via an exception handler SEH/VEH, Windows). As long as the user knows there was a crash and is given the ability to send an error report, that usually is 'good enough' for most gamers. What you don't want to happen is the game to just go poof and the user is left sitting on his or her desktop not knowing what happened (other than the obvious, the game has a bug idea).

You also want to be careful about how the server continues processing the client state as an incorrect implementation can lead to exploits where people purposefully crash out their clients. For example, some games I've seen have had such conditions trigger when crashing out in the middle of a trade transaction due to poor implementations.

Depending on the language you are using, dealing with the actual exceptions will vary. Languages like C# or Java make such extensive use of exceptions, all you have to worry about is not catching them in the right places. If you were using C++ though, you do have a lot more to worry about from an 'exception' standpoint, especially from 3rd party libraries.

Lastly, you want to keep a good set of server side logs as well to know what was sent and when. More than just logs, but discrete events that pertain to state help too. For example, logging that the client "added one stat point" is somewhat useful, but meaningless compared to logging the client "added one stat point bringing the total to 9001", to which the intended max was 42.. Just by taking the time sooner to implement detailed logging that you can easily search through and lookup in the future can end up saving you when things go wrong. Nothing is guaranteed though, there will be times something happens and you aren't logging what you need or you are logging it in a format that is not user friendly, so be prepared to take some time to think about how you want to implement such a logging system to give you the flexibility you need.
hplus0603
hplus0603

What is the best approach for handling exceptions when parsing a received message on the client. Such as the data mentions a character the client has no idea about for whatever reason.


What do you do when you're playing a game of chess, and you find that your opponent doesn't play by the rules?
You stop playing, and likely report the offending opponent.
Translated to computers: If you get data that is in violation of the protocol spec (such as the specification that "I must get an 'introduce character' message before I get an 'update character' message for a given character") then you should treat this as a bug, collect whatever data is necessary to diagnose the bug, and disconnect the connection. There is no way that the game state on that connection will make sense to the player -- you'll just drift more and more out of sync, and players will complain about buggy gameplay (which is worse than dropped connections).
enum Bool { True, False, FileNotFound };
Antheus
Antheus

Following the example, the client would send the server "Who is character X?" and then the server would reply back with the response. Is this overkill considering this will very rarely happen - only when there is a bug somewhere? Then again, what would the client do if it has problems parsing this second message.


The answer depends on how the state is synchronized or propagated between server and client.

One way for server to keep the list of all entities which are mirrored/shadowed/replicated to each client. It's possible that this list also contains mapping from client-ID to server-ID. The downside of this model is that it may require sending more data than needed and perhaps data which server does not want client to know (maphacks).

Other way is for client to implement all object references as "dangling pointers". Client happily accepts IDs as they come, but treats them as unallocated pointers. If server sends detail for certain object, the client replaces the "dangling pointer" with real data. If client suddenly needs one these pointers, it requests the actual definition.struct RemotePtr {
T * operator->() {
if (pointer == NULL) {
pointer = make_blocking_request_and_yield(ID);
}
return pointer;
}
private:
int ID;
T * pointer;
};
The block+yield may be difficult to implement in practice, so trying to pretend it's local pointer will be a bad idea in general without concurrent CORBA-like runtime and all the uglyness it brings. Or without using some language better suited for it.

Third way is for server to always send some sort of baseline (ID/mesh/position), but everything else needs to be requested. This way client can still render at least bind-pose while it's waiting for data.

Then it's also possible for client to be utterly dumb. It never knows what an ID is or what it relates to and only sends actions on IDs to server which responds with UI changes. So instead of knowing that player attack monster, client merely sends click(LMB, 2234541, 55324) and in turn receives [floating_text(red, -55), floating_text(yellow, +5), floating_text(white, "+5xp"), change_property("exp", +5). Under this model the connection is similar to X or other remoting servers.

Or some mixture of this.
jmcmorris
jmcmorris
Thanks guys for your insight. I need to sit down and draw it out but I think I'm going to go with having the client report to the server when something is obviously messed up because either there is a bug or the user is cheating. Once it is done reporting the client will cleanup itself up gracefully as possible. By the way, client is C++ and server is python.



This has been helpful. Thanks again for your thoughts!

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.