Ayende @ Rahien

Hi!
My name is Oren Eini
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:

ayende@ayende.com

+972 52-548-6969

, @ Q c

Posts: 18 | Comments: 72

filter by tags archive

The Candy Crush Challenge

time to read 1 min | 187 words

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

I recon this is about trial / subscription limit circumvention?

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

Easy pesy!

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

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

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

@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

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

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

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

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

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

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

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

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

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

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

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

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

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

@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

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?

Comment preview

Comments have been closed on this topic.

FUTURE POSTS

  1. RavenDB 3.0 New Stable Release - 5 hours from now
  2. Production postmortem: The industry at large - about one day from now
  3. The insidious cost of allocations - 2 days from now
  4. Buffer allocation strategies: A possible solution - 5 days from now
  5. Buffer allocation strategies: Explaining the solution - 6 days from now

And 3 more posts are pending...

There are posts all the way to Sep 11, 2015

RECENT SERIES

  1. Find the bug (5):
    20 Apr 2011 - Why do I get a Null Reference Exception?
  2. Production postmortem (10):
    01 Sep 2015 - The case of the lying configuration file
  3. What is new in RavenDB 3.5 (7):
    12 Aug 2015 - Monitoring support
  4. Career planning (6):
    24 Jul 2015 - The immortal choices aren't
View all series

RECENT COMMENTS

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats