11
\$\begingroup\$

I'm working on a basic roguelike using HTML5 and jQuery, and I've come across a problem.

The way the game currently stands, the system only saves the game state every time the user moves between floors - to minimize overhead. The peril of this is that, if the user gets in trouble, they can simply close the window and return to their game at the beginning of the current floor. This drastically reduces the difficulty of the game (and almost defeats the purpose of a roguelike) - but it's unreasonable to save the game state with every single player movement or attack.

I've researched ways to save the game state on browser window close, but I'm not happy with them. My question is this: assuming "saving a game state" means a moderately heavy ajax/post request, how do I thwart this cheating behavior? Is there a known methodology to quantify incremental/procedural changes to a 2d map, as opposed to saving the entire map state? Please note, I'm not asking for "the most efficient way" - I'm looking for existing methodologies to rectify my inexperience.

\$\endgroup\$
3
  • \$\begingroup\$ Very generic and general JavaScript programming question. I voted down but then I rethought since it's kind of interesting if you consider how you'd do it if you could not trigger an on-unload event. \$\endgroup\$
    – Tim Holt
    Feb 9, 2013 at 6:43
  • \$\begingroup\$ Maybe I'll rephrase it to address that problem directly =) \$\endgroup\$
    – CodeMoose
    Feb 9, 2013 at 6:47
  • \$\begingroup\$ @TimHolt - edited per your suggestion \$\endgroup\$
    – CodeMoose
    Feb 9, 2013 at 7:07

3 Answers 3

8
\$\begingroup\$

The obvious solution is to send every player command to the server as it's made and have the server log them; that way, if the game is aborted for any reason, the logged commands can be replayed to restore the game to the point where it left off.

Of course, when a proper save is made, the old command log can be thrown away (although you could also save it to allow replays of old games; if you do that, you may want to include timestamps in the log for real time replay). You may also want to automatically make a full save after n commands have been made since the last save (where, say, n = 1000) to avoid excessively long replays if the player stays on the same level too long before closing or crashing the game.

If your game involves random numbers (and, being a roguelike, it probably does), you'll also need to make sure that they can be recreated correctly during a replay. One solution is to include every random number in the replay log; another is to use a deterministic pseudorandom number generator and save the seed when the game is saved.

Note that all of these methods can be exploited by tampering with the client, but that's really inevitable whenever you have game logic on the client. As a cheat-proof alternative, you could run the actual game on the server and just have the client send all player commands to the server and receive display updates back. For a roguelike, this may well be feasible. Obviously, this would also solve your state saving problem (although you might still want to implement some form of command logging on the server, both for replay functionality and to allow recovery from server crashes). The downside is that doing this would make off-line play impossible (unless, of course, you also make the server-side code available for players to run locally if they want).

\$\endgroup\$
2
  • 1
    \$\begingroup\$ AFAIK all RGN's are pseudorandom.. you need specialized hardware to get truly (unseeded) random values. \$\endgroup\$
    – bobobobo
    Feb 9, 2013 at 15:45
  • 1
    \$\begingroup\$ @bobobobo there's pseudorandom (seeded) and there's cryptographically secure random numbers (unseeded) in the .Net Platform. It may/may not be hardware random, but it's close enough to be unable to just store a seed. \$\endgroup\$
    – Rangoric
    Feb 10, 2013 at 3:16
2
\$\begingroup\$

See https://stackoverflow.com/questions/3888902/javascript-detect-browser-close-tab-close-browser for a more generic asking of the same question.

Also consider saving full game on floor changes, and incremental deltas on each move. Then you can replay the game up to the point of last move.

Assuming there is some use of random number generators, you can generate and re-seed the random number generator at the start of each new floor, and then save it along with the floor. Then you can reseed on load, and should be able to replay the moves with the same sequence up to the last one done.

\$\endgroup\$
3
  • \$\begingroup\$ Thanks Tim - once I get in the game dev mindset, it's really easy to bucket anything that touches it into game dev. Props for providing an answer anyway. \$\endgroup\$
    – CodeMoose
    Feb 9, 2013 at 6:45
  • \$\begingroup\$ No worries - see my revised comment on question too :) \$\endgroup\$
    – Tim Holt
    Feb 9, 2013 at 6:47
  • \$\begingroup\$ I would have said incremental deltas on each move too. \$\endgroup\$
    – bobobobo
    Feb 9, 2013 at 15:43
2
\$\begingroup\$

Approaches to, not incremental saves, but finding time for a full save:

  • onunload, as previously mentioned. I have found this to be reliable (but using localStorage, not network, which may change things).
  • Save when your window loses focus.
  • Save periodically: when the last save has not been in n minutes and the user has not made any input in m seconds.
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .