The whole idea for the server was to prevent cheating as much as possible by the players and to reduce the work that the client has to do in validating every single action a player is allowed. What this meant on the server side is at the beginning of each method that dealt with a client request there would be a myriad of checks to make sure the client was not trying something funny.
For example the overall structure (in pseudo-code) would be like this:
check is player logged in
check is player allowed to do this action
check does player have enough credits for this action
# finally do the real work
update player game state
generate response with updated state
Where the majority of the duplication was occurring was in those checks. On average there would be 100 lines of code just to make sure that the player had a valid session and had enough credits to do what they wanted to do. This was starting to get out of hand because every time I wanted to add a player action, I would be copy/pasting a lot of code. This also meant if I had to add an extra check as a part of the 'standard checks' then I would have to go through all of the implemented server actions and update every single one to include the check plus the handling code for it!
There had to be a better way.
To give a bit more detail each of these 'pre-flight' checks would look like this:
if (some condition doesn't match)
look up error to return
construct response object appropriate to the function return type
That's a lot of code for every check! Also not easy to refactor due to that early return statement.
The goal was simply to reduce the amount of code needed for all the pre-flight checks. So I decided to introduce exceptions to my code. I didn't particularly want to have exceptions in the beginning because it's quite easy to make a mess of the whole exception design and the libraries that I was using didn't throw exceptions so it was natural for me to follow the same trend.
To do this I had to do a few things:
- Create exception classes to deal with user/internal exceptions
- Extract all the pre-flight check conditions into helper classes that would throw exceptions instead of returning false
- Add a catch at the very top level that would convert the exception to an appropriate response object for the method
After doing all of this my pre-flight checks were a mere 5 lines on average, a fantastic simplification which will make the code more manageable in the future. This also meant that I could refactor other parts of my code to be reusable which was not possible before with so many separate return statements for every single error condition.
Now if there is an error condition or it's detected that the client code is trying to do something funny, I just throw an exception in the server code, the server response handler catches this and converts it to an error response. Simple.
The only thing I am not quite sure about is having a number of methods that have a conditional check that throws an exception, but that's something I can live with.