Ayende @ Rahien

It's a girl

The Candy Crush Challenge

Here is an interesting challenge. In Candy Crush (which I do not have a problem with), you have 5 lives to try. Life renew at a rate of about 1 per 30 minutes. So it is pretty common to get to this stage:

candy crush life step 1

Now, you can go and change your system clock, and then you’ll get 5 more lives, and you can play some more.

Now, there are probably very good reason why this is done in this manner, to ensure players are still hooked, and it is just inconvenient enough that there is still meaning to the number of lives you have.

However, let us say that we wanted to stop that. How would you go about approaching this? Remember, we are talking about an app on a phone, and this isn’t something super serious if it gets broken, but we want to avoid the super easy workaround.

How would you solve that if this was on a computer, instead of a phone? What are the different considerations? What if this was something that was very important?

Comments

Frans Bouma
10/08/2013 09:24 AM by
Frans Bouma

I recon this is about trial / subscription limit circumvention?

Andy
10/08/2013 09:36 AM by
Andy

There is probably an event or notification for when the system clock changes, so you could probably use that and some deltas of time elapsed, rather than specific point in time.

Phillip Haydon
10/08/2013 09:38 AM by
Phillip Haydon

Easy pesy!

Do what Electronic Arts does, require the user to be online to play, ping the server for the time :D

Giorgi
10/08/2013 09:41 AM by
Giorgi

You can get current time from NTP server as you have done in Rhino.Licensing : https://github.com/ayende/rhino-licensing/blob/master/Rhino.Licensing/SntpClient.cs

There is also WM_TIMECHANGE message which is wrapped by SystemEvents.TimeChanged Event

Duarte Nunes
10/08/2013 10:13 AM by
Duarte Nunes

On the phone you could store the latest date time seen for a given user. The time when you get more lives is always an absolute time as a function of the latest time seen. You could also punish the user by pushing forward the time when the she gets more lives when you detect some delta above a given threshold between the current time and the latest time seen. By doing this you're making it more and more inconvenient for the user to cheat, until to do so they would need to change the date time settings to some value their phone doesn't even support.

On the computer you can assume and require internet connectivity more easily, so you can get the time from an ntp server. You can also use cpu time.

Patrick Huizinga
10/08/2013 10:49 AM by
Patrick Huizinga

@Duarte Numes

So what about an honest player that actually waits longer than your proposed delta? Do you want to punish him as well?

What I try to get at is: how does your scheme differentiate between a player that waits 30 minutes and one that simply moved the system clock 30 minutes forward?

Felipe Fujiy Pessoto
10/08/2013 10:51 AM by
Felipe Fujiy Pessoto

If I use something like Thread.Sleep, it wouldn´t be influenced by clock change?

Duarte Nunes
10/08/2013 11:04 AM by
Duarte Nunes

@Patrick Huizinga

In the case of an honest player, the current time of the system is always equal to the highest time seen. Only in the case of a dishonest one is the highest time seen greater than the current time, meaning that the user went forward in time (perfectly acceptable) and then back again to restore their time settings.

Sergey Shumov
10/08/2013 11:30 AM by
Sergey Shumov

There are at least two options: 1) Get time from NTP server (or cell phone network or whatever external service) 2) Maintain an internal timer inside the app and dump its value every X seconds to the disk; in this case the timer would be incremented only when the app is running

Which option to choose depends on the use cases of the app. If it is an expiration module then you should first decide what it is that customer buys: ability to use the app before a particular date or ability to use your app for a finite amount of time.

Alexander Taran
10/08/2013 11:37 AM by
Alexander Taran

I'm thinking that push notification heartbit once a day might be an interesting idea.

Joseph Daigle
10/08/2013 11:47 AM by
Joseph Daigle

The timestamps just need to be relative, not absolute. So as Duarte mentioned, just persist a timestamp of when the timer starts. Then everytime the app loads, you can do some simple arithmetic to figure out how much time is left.

Brian
10/08/2013 11:51 AM by
Brian

I wouldn't depend on anything that uses the system clock as its interpretation is subject to "legitimate" changes due to anything from timezone changes, daylight savings time transitions, or drift adjustments (even UTC, while solving some of these, wouldn't be immune to user shenanigans). Rather, at the point the need to wait 30 minutes is determined, I would persist the then-current value for the tick count and do the math from there. I suppose one issue with this approach is you'd have to also guard against reboots since that would reset the tick count...just make them wait another 30 minutes? Definitely lots of little considerations...

Khalid Abuhakmeh
10/08/2013 12:18 PM by
Khalid Abuhakmeh

The problem is you are comparing the time in the app, to the clock on the phone. Even if you were to store a timestamp locally in the app, you still have the changing and volatile external clock (on the iPhone). If you always depend on the users phone / clock you will be in trouble.

You might want to call out to the server (async of course) to get some sense of time that isn't dependent on the user's hardware. This is a heartbeat that doesn't impact gameplay.

The problem with the iPhone is that apps are sandboxed, essentially they are put into a frozen state every time you exit. So any process you start / hope to continue will be frozen as soon as a context switch occurs.

I have Infinity Blade III on my iPhone and realized that I can't play it without an internet connection, which totally sucks.

Hugo Dahl
10/08/2013 12:28 PM by
Hugo Dahl

The maxim of "Don't trust the user's input - EVER" still applies here. Anything the user can exert immediate control over (using legitimate means, no cracking or systems compromise, just input boxes, system data & state, etc), always ALWAYS check for validity and legitimacy. Platform doesn't matter.

User's clock says 17:22:45 CST? Does that fall within a maximum delta of what my authoritative source (my hosted server, NTP, etc). No? Hmmm, I suspect user's gaming me. Could there be false positives? Sure, but I'd bet that those would be far remote corner cases, rather than the norm.

This works in connected system, where access to the server is necessary. If anything must/can be disconnected at any time and still work, then this clearly doesn't fit the bill.

In a disconnected mode, you'd need some sort of long running system (service, daemon, etc) that can track time changes, or count time intervals, and when it needs to be checked, validate that the current time is within a delta of what it expects. Overly simplified, store current time at system startup, every 5 seconds, accumulate a bit, when prompted, check that the sum of start + accumulator are within norm of current time. Of course, the user can control the service, or possibly compromise the accumulator. Then all bets are off.

Finally, perhaps using the OS's "system uptime" functionality (when available) can prove to be a useful way of tracking abnormal or fraudulent time changes.

Frans Bouma
10/08/2013 12:34 PM by
Frans Bouma

Unless you're pulling the actual time from a time server and have an immutable reference when you started, sandboxie or a vm will always be able to circumvent it. This has the downside of arequired internet connectioon, something which can be very annoying to users. And anyway, if people want to circumvent your protection, they will...

Evangelos Pappas
10/08/2013 01:49 PM by
Evangelos Pappas

I can hardly choose any solution that save some kind of timestamp (relative or not) as some countries change willingly their clock during seasons (http://www.timeanddate.com/news/time/). Also solutions that propose some kind of runtime computation are also a really bad practise as in mobile there is a great issue with the battery consumption (and personally I don't find it either a good practise for a server). The push notification or a NTP synchronization are really good solutions, but they both can have battery consumption issues, if they are not implemented with the right way.

My solution is to let the player hack my internal clock, as candy crush is a online game and keeps an online score, I would synchronize user's stats just before the player enters the playable level. In the client side (mobile/web browser) I'd have implemented an escape scenario to handle this, so I would pop-up a simple notification "You are out of Lives after all".

Steve Sheldon
10/08/2013 02:08 PM by
Steve Sheldon

Always play the game just before daylight savings time kicks in.

Patrick Smacchia
10/08/2013 04:44 PM by
Patrick Smacchia

There are tools to change dateTime in a process and hackers use them a lot. To circumvent these tools, one can touch or write a file and look at the file date.

If the OS date gets changed, using the file touching date trick (or any form of other storage, I find this one especially convenient) you can detect when the machine time go back.

Unless you get internet cnx and you're pulling the actual time from a time server as suggested Frans, I cannot imagine a way to detect scenario where OS date is increased (between 2 runs), which shoudn't be a problem anyway in trial date management where OS date decreasing is the problem.

Anzio
10/09/2013 04:20 AM by
Anzio

Build an internal clock using thread sleep and a counter. This require that the application is running in background to evaluate the wait time.

GordonS
10/09/2013 02:38 PM by
GordonS

@Giorgi assuming you specify hostnames for the SNTP servers (like Rhino Licensing does), then you can easily circumvent this (at least in Windows and Linux) by configuring your hosts file to redirect the specific hostnames to a different IP address (e.g. 127.0.0.1). I'd assume there are similar mechanisms on smartphones.

David Cumps
10/10/2013 01:26 PM by
David Cumps

What about people who have a firewall and block outgoing traffic?

I run my own internal NTP server on my firewall (which in turn goes out there to a real NTP server to get its time), but all NTP traffic from inside is blocked, all devices query the firewall's NTP server to get the time.

What do you do when you check an NTP server and it's not there? Punish the user? Or accept that it's not always there and ignore it?

Comments have been closed on this topic.