8
\$\begingroup\$

My first thoughts about marking levels complete is to just write the information to a file upon level completion and load that in once the app is opened up again. But how could I keep this safe from tampering and prevent cheating?

\$\endgroup\$
  • \$\begingroup\$ "How Candy Crush does it," isn't on-topic here, so I've removed that aspect of the question and focused instead on the 'preventing cheating' aspect. \$\endgroup\$ – user1430 Jan 10 '15 at 0:21
  • \$\begingroup\$ If you're interested in how Candy Crush in particular handles this problem, you might have more luck asking on gaming.stackexchange.com \$\endgroup\$ – Kevin - Reinstate Monica Jan 10 '15 at 3:34
19
\$\begingroup\$

Storing the completion information in a local file is a simple and perfectly acceptable method of doing so. Fundamentally, this is what every game will do to track progress (in some fashion, although the specific formats used for the data and the storage mechanism will differ).

Protecting the file from tampering is more difficult. If there's no compelling reason (no advantage to be gained by the player, no rewards, et cetera) you can usually get away with not caring.

But if you do care, and you and if you can reasonably expect that your platform is closed (consoles and non-jailbroken-iOS devices, for example) you also don't need to worry too much. The user will not normally be physically capable of accessing the file to tamper with it (provided you use the appropriate OS APIs to store the data in "private" locations and public ones (for example, storing this file in the user's iCloud Drive on iOS is probably not a great idea).

If you are absolutely concerned about tampering to the point where is must not happen (for example, where there is an advantage to be gained by a player who has more levels completed), you need to store the data server-side. Anything stored client-side can ultimately be compromised and shouldn't be trusted: it's the user's machine, in the user's hands. The control dynamic is just not in your favor.

It's not strictly impossible to hack files stored server-side, either, of course, but it's much more difficult if you've got a reasonable accounting system.

\$\endgroup\$
  • \$\begingroup\$ Thanks, I just don't want it to make easy to temper with the progres files. Been reading on and wondering if the Preference class specific to LibGDX can help me out. I could store simple booleans for the levels and perhaps with a single high score to it as well. \$\endgroup\$ – Madmenyo Jan 10 '15 at 0:50
  • \$\begingroup\$ @MennoGouw: if we are talking about android here, files private to the app are not accessible unless the device is rooted. Presumably libGDX uses such files for preferences. \$\endgroup\$ – njzk2 Jan 10 '15 at 4:32
  • \$\begingroup\$ You could also use encryption, with a key stored in your server but the data itself stored locally. Then request the key on game start and decrypt the save file. \$\endgroup\$ – Kroltan Jan 11 '15 at 1:17
  • 1
    \$\begingroup\$ @Kroltan But then you could RAM-dump the device while it's in use, get the key, and tamper with the file. (In an emulator, if need be.) \$\endgroup\$ – wchargin Jan 11 '15 at 6:21
  • 1
    \$\begingroup\$ The user could also just modify the stream coming from the webserver, since even if the connection is encrypted, the user's computer has the key to decrypt it. Then he just needs to change the values of that for say {"finalLevelCompleted":true}. Done. \$\endgroup\$ – Kroltan Jan 11 '15 at 11:43
9
\$\begingroup\$

You can create a replay file as proof of work while the player is playing. Start the game, save the starting conditions including the name of the level and the pseudorandom seed, record the exact timestamped input states (mouse movements, key or button presses, etc.) that your game's input layer passes to its logic layer, and stop recording once the objective is achieved. You can compress an input log with run-length encoding (RLE) so that only changes in the button states (presses, releases, and the like) cause log events. From the initial conditions and input log, you can verify completion of the objective by replaying the inputs into the game's physics and AI.

Logs like this have several applications that are familiar to players of video games, especially emulated games. Players can rewatch a game locally from a log to look for strategy flaws or to brag to local friends. Your game's attract mode can consist of logs for selected levels. Netplay in games using the GGPO engine is structured similarly, with a delay of a few frames between the input and the game engine in order to allow time for the other player's input to arrive through the Internet. And you can require leaderboard participants to upload the log, and then your server will run it in fast-forward on a cut-down copy of the game with no graphics engine. To prevent a replay attack where one player uploads another's log, the information used to seed the game's pseudorandom number generator can include the player's leaderboard user ID. This way the server can compare them, and a manually edited file will desynchronize.

Some extremely dedicated players will manually construct an input log that constitutes a proof of work. This is called a "tool-assisted speedrun". If you don't want TAS on your public leaderboards, you can require plays at the highest competition tiers to be done while connected to the Internet. The server would send a key value every few seconds, and the client would send the input log in real time as if it were netplay, encrypted with that key. This key changes every few seconds, and in games using some sort of random element, it may reseed the pseudorandom number generator. If sustained lag between sending a key and receiving the corresponding input packets exceeds several seconds, we can assume that the user is trying something "funny".

\$\endgroup\$
  • \$\begingroup\$ TAS is more than just manually creating a log to be replayed, it means that the game has been manipulated in such a way that you could save states and play it frame by frame for the perfect actions each time. It's not your point, but it's worth mentioning. en.wikipedia.org/wiki/Tool-assisted_speedrun \$\endgroup\$ – TankorSmash Jan 11 '15 at 4:40
3
\$\begingroup\$

If you want to do this totally in the client side and you don't consider the private storage provided by your platform to be secure enough (e.g. if there's actual money involved in completing levels), what you need is a proof of work. Your save file must contain an information that is designed such that creating the information is difficult to calculate unless you've completed the associated level, but easy to verify by a third party that does not necessarily have completed that particular level (preferably without having to know the solution itself). The larger the difference in the difficulty of calculating the proof of work before and after completing the level, the more secure the proof of work is.

Unfortunately though, this proof of work must be such that it's tied to the gameplay, so there is no generic way this can be done for arbitrary games, and your game executable must not contain the solution to the game in any way, or otherwise it's quite easy to extract the proof of work from the executable. In most cases, you almost always going to have to specifically design your gameplay around this particular requirement to make a proof of work to be cheat-proof, so you'll also lose some creative freedom.

For example, say a game of Sudoku, the proof of work could be the completed Sudoku board (note that the board must be generated by a trusted third party, because most Sudoku board generation algorithm need to precalculate the solution, this weakening the proof of work if it's done on the client). Or a game of factoring a large composite number, the proof of work is the prime factors (I'd call such a game RSA vs NSA).

To be truly secure though, your game has to contain some random elements, because if your game is totally deterministic, it's possible to cheat be copying the proof of work from someone else that have completed the level (this is a replay attack).

The goal of proof of work is to make cheating at least as difficult as creating a bot for the game. So, to cheat on a Sudoku game, you'll need to have a Sudoku solver, etc.

This is extremely hard. If it's a single player game, it's better keep your creative freedom and don't worry about cheating. It's their game, they've bought it and if they cheat, that doesn't affect your bottom line. Or turn it to a server based game, and do all your important game logic in the server.

\$\endgroup\$
1
\$\begingroup\$

If you prefer using an external file to hold save information and it is in a place that would be tamper-able by users you may want to read up on how encryption works and use that to prevent anyone from being able to make meaningful changes to the file. Something like a Feistel Cipher should be a good starting point and would be sufficient to stop someone without alot of time and know-how from changing a save file. http://en.wikipedia.org/wiki/Feistel_cipher

Aside from that Josh has hit the nail on the head when it comes to client and server side storage.

\$\endgroup\$
0
\$\begingroup\$

If someone is able to pick through the binary and find out the algorithm - you'll never really be able to prevent that, so don't bother.

To stop simply use a cryptographic hash!

Suppose you have this:

function computeHash(levels) {
    code = "";
    for(level in levels) {
       code = hash(code+level+level.isComplete()+"MAGICSALT")
    }
}

It's really simple but it means you need to write a code at the end of the file, you then parse that file as usual, and do computeHash(levelListReadFromFile);

IF that hash = code read in the file then it is VERY PROBABLY "legit". By this I mean ... someone with a hex editor wont crack it, it'll take someone who knows their stuff.

Always favour local storage BTW - unless it's an MMO obviously, people don't want you using their data, go on trains, and do all kinds of stuff. I hate programs that require internet access for no good reason.

\$\endgroup\$
  • \$\begingroup\$ Downvoter why??? \$\endgroup\$ – Alec Teal Jan 11 '15 at 11:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.