2014-12-23 15 views
12

Chciałbym stworzyć sposób na odliczanie czasu lokalnego od czasu do hackowania lokalnego, na przykład do odliczania czasów na kolejną nagrodę w grach.Lokalny odmierzający czas-hack, odliczający czas w tle, licznik czasu odliczania

Wyjaśnienie obiektu:

lokalnej
- Działa bez połączenia internetowego

native iOS
- Chcę znaleźć rozwiązanie (najlepiej) Objective-C lub Swift

proof czasowo-włamany
- Gdy użytkownik zmienia swój czas urządzenie do przodu/do tyłu, pozostały czas pozostaje taki sam

tle
- wyłączenia/ponownego otwarcia przyjazny

timera odliczanie
- Zegar, który po starcie zapewni metodę sprawdzania pozostałego czasu:

Chciałbym mieć system nagród oparty na czasie w mojej grze na iOS, który nie będzie łatwo hackable po prostu przesuwając urządzenie do przodu.

Początkowo sądziłem, że nie było to możliwe i nadal używałem [NSDate date] i savedDate, porównując je razem, aby znaleźć upływ czasu. Ostatnio jednak natrafiłem na grę, która korzysta z tej funkcji: Crossy Road. Z tego co wiem, jest to gra Unity, ale wątpię, aby nieco popularna funkcja dostępna w projekcie Unity, która została skompilowana w aplikację na iOS, nie jest dostępna w systemie iOS za pośrednictwem Objective-C lub Swift.

Zapewniają system nagród w systemie czasowym, raz na 6 godzin i, o ile testowałem, nie można go hakować, zmieniając czas urządzenia. Działa również bez połączenia z Internetem. (Nie jestem pewien, czy to działa, jeśli nigdy się nie połączysz, ale kiedy podłączyłem, odłączyłem i spróbowałem użyć hacka, to nie działało.)

+0

Ustaw własne odwołanie .. Po załadowaniu, zapisz znacznik czasu do bazy danych, jeśli nie jest ustawiony (więc czas zapisu pierwszego uruchomienia). Zliczaj czas w oparciu o ten punkt odniesienia, bez względu na to, która jest godzina. – davidcondrey

+0

Jak to działa przeciwko oszustwom? Rozumiem, że zapisanie pierwszego znacznika czasu do bazy danych, ale jaka metoda anty-hackowa zadziała w celu uzyskania "aktualnego czasu", aby uzyskać różnicę? – Gyfis

Odpowiedz

6

Możesz użyć aktualnego czasu pracy systemu (mach_absolute_time()). Ta funkcja jest łatwo dostępna za pomocą CACurrentMediaTime(), która zwraca ją w ciągu kilku sekund. Wtedy prawdopodobnie musisz ustawić pewien interwał, aby sprawdzić tę wartość. Można to oczywiście zrobić za pomocą NSDate, ponieważ nie jest to problem, który uruchamia użytkownik ustawiając zegar do przodu. Jeśli chodzi o ponowne uruchomienie, po prostu zapisz wartość początkową i użyj jej jako przesunięcia po ponownym uruchomieniu.

Jedyną wadą jest to, że nie wlicza się do czasu wyłączenia urządzenia, ale generalnie nie jest to zbyt wielka okazja, gdy nikt nie odwraca swoich telefonów.

Aby uzyskać aktualną funkcję timera, po prostu zapisz wartość na początku, a następnie regularnie sprawdzaj wartość przewidywaną, ewentualnie zwiększając rozdzielczość, gdy zbliża się koniec.

+0

Podoba mi się to rozwiązanie! Ale byłoby naprawdę o wiele lepiej, gdybyś mógł liczyć w całkowitym czasie, z czasem "wyłączonym". Czy mógłbyś wiedzieć, jak? – Gyfis

+0

@ Gyfis próbowałeś wyłączyć telefon w trybie offline, aby sprawdzić, czy gra Unity nie przestaje się rozwijać? Możliwe, że jest to rozwiązanie, które zastosowali podczas gry offline. – Stonz2

+0

Tak, spróbowałem! I zaskakująco, czas w grze Unity został policzony poprawnie nawet w czasie, gdy mój telefon był wyłączony! Teraz wydaje mi się to bardzo interesujące! – Gyfis

3

Mam dokładnie te same wymagania dla aplikacji, którą buduję. Próbowałem używać CACurrentMediaTime(), ale po wyłączeniu urządzenia resetuje się do 0, co nie jest tym, czego potrzebujesz.Co pracował nawet po wyłączeniu urządzenia było wykorzystanie tego czasu odniesienia:

currentUser.timeLastUpdated = [[NSDate date] timeIntervalSince1970]; 

Oto kod, aby pomóc wdrożyć stoper. Jest to modyfikowane z innego wątku stackoverflow.

W MainViewController.m

@interface MainViewController() 

@property (strong, nonatomic) IBOutlet UILabel *countdownLabel; 

@end 

@implementation MainViewController 
{ 
    UserData *_currentUser; 
    int hours; 
    int minutes; 
    int seconds; 
} 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    // Do any additional setup after loading the view. 
    _currentUser = [UserData currentUser]; 
    [self updateTimerText]; 
    [self startTimer]; 
} 

- (void)startTimer { 
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer:) userInfo:nil repeats:YES]; 
} 

- (void)updateTimer:(NSTimer *)timer { 
    if (_currentUser.secondsLeft > 0) { 
     _currentUser.secondsLeft--; 
     [self updateTimerText]; 
    } 
} 

- (void)updateTimerText { 
    int secondsLeft = _currentUser.secondsLeft; 
    hours = secondsLeft/3600; 
    minutes = (secondsLeft%3600)/60; 
    seconds = (secondsLeft%3600) % 60; 
    self.countdownLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02d",hours,minutes,seconds]; 
} 

} 

teraz, aby uczynić go kuloodporne więc nie przestał liczyć, kiedy zatrzymać aplikację, trzeba zmodyfikować AppDelegate.m.

W AppDelegate.m

@implementation AppDelegate 
{ 
    UserData *currentUser; 
} 

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 
    // Override point for customization after application launch. 

    // Intialize currentUser 
    currentUser = [UserData currentUser]; 

    // Reset secondsLeft based on lastTimeUpdated 
    if (currentUser.secondsLeft > 0 && currentUser.lastTimeUpdated != 0) { 
     currentUser.secondsLeft -= ((int)[[NSDate date] timeIntervalSince1970]) - currentUser.lastTimeUpdated; 
    } 

    return YES; 
} 

- (void)applicationDidEnterBackground:(UIApplication *)application { 
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 

    // Set the last time updated (only need to call it here because this is coupled with applicationWillTerminate 
    currentUser.lastTimeUpdated = (int)[[NSDate date] timeIntervalSince1970]; 
} 

- (void)applicationWillEnterForeground:(UIApplication *)application { 
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 

    // Reset secondsLeft based on lastTimeUpdated 
    if (currentUser.secondsLeft > 0 && currentUser.lastTimeUpdated != 0) { 
     currentUser.secondsLeft -= ((int)[[NSDate date] timeIntervalSince1970]) - currentUser.lastTimeUpdated; 
    } 

} 

Wystarczy tylko zmodyfikować trzech metod w AppDelegate ponieważ ApplicationWillTerminate zawsze odpala z ApplicationDidEnterBackground (o ile mogę powiedzieć).

Bother currentUser.secondsLeft i currentUser.lastTimeUpdated są ints. Wrzuciłem [[NSDate date] timeIntervalSince1970] jako int, ponieważ zwraca CFTimeInterval, który jest typem double.

Należy wreszcie zapisać w bazie danych. Ja leniwie zapisać te dwie zmienne do NSUserDefaults gdy są one aktualizowane przez włączenie następujący kod w moim pliku UserData.m:

W UserData.m

-(void)setSecondsLeft:(int)secondsLeft { 
    _secondsLeft = secondsLeft; 
    [[NSUserDefaults standardUserDefaults] setObject:@(secondsLeft) forKey:@"secondsLeft"]; 
} 

-(void)setLastTimeUpdated:(int)lastTimeUpdated { 
    _lastTimeUpdated = lastTimeUpdated; 
    [[NSUserDefaults standardUserDefaults] setObject:@(lastTimeUpdated) forKey:@"lastTimeUpdated"]; 

ten sposób, gdy aplikacja kończy działanie dane są zapisywane do późniejszego wykorzystania.

Powiązane problemy