I've been working with
jearl on a game project for about a year and a half now. It's a flashed-based multiplayer roguelike, featuring dungeon crawls finishable in about a half hour. Hence the working title, 'Lunch Crawl'.
Anyway, about three months ago, after a longer period of stagnation, I was inspired to rebuild it using Google App Engine, to solve a conceptual problem I was having regarding cheating. It all boils down to running the game logic on the client (the browser) or the server.
Client-side architecture:
In this design, the game runs entirely in the browser, uploading results (like a score) to the server, either periodically or at the end of the run.
Flash-based games in which you keep score have a long history of cheaters. In the simplest of abuses, you notice that a flash game submits a score to a website with the score in the url, or in some data posted 'invisibly' in a form. There are some minor things that can be done to make it harder to cheat, but that tends to just motivate people to try harder. You can decompile flash and replace the "attack-the-monster" function with one that always hits for 1000 damage. Or you could replace the "player-take-damage" function to make it so the player always heals immediately afterward. This isn't strictly a flash problem, either; First Person Shooters like Half Life, Quake and even Halo have cheating problems. An early example is to replace all the wall textures with transparent textures...
Server-side architecture:
This gets rid of most of the cheating problem; the server rolls all of the dice, keeps track of all of the stats, and even limits what the client knows about the player's surroundings. In Half-Life terms, it does no good to replace the walls with transparent textures if the server doesn't tell the client the locations of anybody you can't see. Games like World of Warcraft use this approach.
The downside is that it's very expensive to run everybody's actions through a server. It's the main reason WoW needs several servers per realm, despite not having particularly complex AI outside of instances. It's also the main reason that multiplayer FPSs still struggle with players-per-server limits.
This downside is especially painful for development of Lunch Crawl, as the plan is to use a web-based service to process turns. In short, every time you do something in the game, it sends the command to the server as if you were going to a web page, gets the results, and renders them in flash. So in addition to the time it takes to process the action and send the data, there's a per-turn price that comes with using HTTP. I'm optimizing as much as I can, but until HTTP requests magically become faster, I doubt I can make a turn take less than 250ms. Seems like not very long, but trust me, you'll notice it when you're trying to walk across a big room.
Hybrid architecture:
There's a few ideas out there for doing a little bit of both. The biggest problems with all of these is that they're complicated to write and they don't completely prevent cheating.
- Guess-and-check. FPSs do this, to some degree. You allow the client to predict the outcome of an action while waiting for the response from the server. This still requires the client to wait whenever things get complicated (like, you shoot somebody) or when network congestion makes responses come back out of order. Usually, when things get messed up, the whole session needs to be rebuilt using what the server knows about the situation, and it tends to be jarring. In a roguelike, this could mean that you run down a corridor only to get yanked back a dozen squares because the server just realized you noticed a secret door.
- Log and re-run. In this approach, the client is allowed to go hog-wild. The dice are rolled on the client, the character's kept track of on the client, etc. Everything that happens is logged, though, and periodically the log is sent to the server. The server then re-creates the scene and runs through the player's actions again, using magic dice that roll the way the client was expected to roll. If the end results don't match up, the client must be lying. There's a couple problems with this approach, however. First, it means coding the same application on both the client and server, which often means two different languages. This could be extremely complicated - one small bug in a routine to determine if you could see a monster, for example, could result in clients being labeled cheaters when they aren't. The other major problem is that it won't stop all cheating. If the client knows where all the monsters are, there's nothing stopping a cheater from displaying them all on the map. Same thing with invisible monsters, secret doors, and fog of war.
- Log and detect cheating later. The weakest of the anti-cheating methods. The idea is to know ahead of time what the highest possible score for a game is and predict, roughly, what score the player should realistically get. If you keep enough of this data, it should be possible to detect the worst of the cheaters. For example, if a dungeon is created that is inhabited by 30 goblins that are each worth 2xp, then it's pretty obvious that a player reporting 75 xp is cheating. Likewise, if a player always reports always killing every monster, thus getting maximum xp, but never reports that they used a healing potion, they could be a cheater. Statistical analysis could be used to spot the likely cheaters. Traps could also be laid for players suspected of cheating, such as placing some monsters in unreachable rooms or swapping out the client for a different one (see the next option).
- Encryption, obfuscation. This isn't really a method to prevent cheating so much as a way to make it harder to cheat. If an encryption key is hard-coded into the flash client and changed periodically, then any cheater that is using an altered version of the flash client (with, say, god mode) will suddenly be unable to communicate with the server. This does mean forcing players to download new clients periodically, as well as requiring some scheme to prevent cached flash clients from being immediately invalidated.
Anyway, I'm stumped. I was toying with a combination of the hybrid architectures when I last got stuck due to overcomplexity.