Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to share saved games across devices?

I implemented GameKit into my iOS game including the saved game feature.

Here an example how I save and load a game:

MobSvcSavedGameData.h

#ifndef MOBSVC_SAVEDGAMEDATA_H
#define MOBSVC_SAVEDGAMEDATA_H

#import <Foundation/Foundation.h>

@interface MobSvcSavedGameData : NSObject <NSCoding>

@property (readwrite, retain) NSString *data;

+(instancetype)sharedGameData;
-(void)reset;

@end


#endif /* MOBSVC_SAVEDGAMEDATA_H */

MobSvcSavedGameData.m

#import "MobSvcSavedGameData.h"
#import <Foundation/Foundation.h>

@interface MobSvcSavedGameData () <NSObject, NSCoding>

@end

@implementation MobSvcSavedGameData

#pragma mark MobSvcSavedGameData implementation

static NSString * const sgDataKey = @"data";

+ (instancetype)sharedGameData {
    static id sharedInstance = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });

    return sharedInstance;
}

- (void)reset
{
    self.data = nil;
}

- (void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeObject:self.data forKey: sgDataKey];
}

- (nullable instancetype)initWithCoder:(nonnull NSCoder *)decoder {
    self = [self init];
    if (self) {
        self.data = [decoder decodeObjectForKey:sgDataKey];
    }
    return self;
}

@end

For simplicity my saved game object above has only a NSString which will be serialised and uploaded like so:

void MobSvc::uploadSavedGameDataAwait(const char *name, const char *data)
{
    GKLocalPlayer *mobSvcAccount = [GKLocalPlayer localPlayer];

    if(mobSvcAccount.isAuthenticated)
    {
        MobSvcSavedGameData *savedGameData = [[MobSvcSavedGameData alloc] init];
        savedGameData.data = [NSString stringWithUTF8String:data];
        [mobSvcAccount saveGameData:[NSKeyedArchiver archivedDataWithRootObject:savedGameData] withName:[[NSString alloc] initWithUTF8String:name] completionHandler:^(GKSavedGame * _Nullable savedGame __unused, NSError * _Nullable error) {
            if(error == nil)
            {
                NSLog(@"Successfully uploaded saved game data");
            }
            else
            {
                NSLog(@"Failed to upload saved game data: %@", error.description);
            }
        }];
    }
}

And this is how I download the most recent saved game on the next play session again:

void MobSvc::downloadSavedGameDataAwait(const char *name)
{
    GKLocalPlayer *mobSvcAccount = [GKLocalPlayer localPlayer];

    if(mobSvcAccount.isAuthenticated)
    {
        [mobSvcAccount fetchSavedGamesWithCompletionHandler:^(NSArray<GKSavedGame *> * _Nullable savedGames, NSError * _Nullable error) {
            if(error == nil)
            {
                GKSavedGame *savedGameToLoad = nil;
                for(GKSavedGame *savedGame in savedGames) {
                    const char *sname = savedGame.name.UTF8String;
                    if(std::strcmp(sname, name) == 0)
                    {
                        if (savedGameToLoad == nil || savedGameToLoad.modificationDate < savedGame.modificationDate) {
                            savedGameToLoad = savedGame;
                        }
                    }
                }
                if(savedGameToLoad != nil) {
                    [savedGameToLoad loadDataWithCompletionHandler:^(NSData * _Nullable data, NSError * _Nullable error) {
                        if(error == nil)
                        {
                            MobSvcSavedGameData *savedGameData = [NSKeyedUnarchiver unarchiveObjectWithData:data];
                            NSLog(@"Successfully downloaded saved game data: %@", [savedGameData.data cStringUsingEncoding:NSUTF8StringEncoding]);
                        }
                        else
                        {
                            NSLog(@"Failed to download saved game data: %@", error.description);
                        }
                    }];
                }
            }
            else
            {
                NSLog(@"Failed to prepare saved game data: %@", error.description);
            }
        }];
    }
}

I tested this by uploading a random string and receiving it on the next session by using the same name. It works! However, as soon as I try to download the saved game from my second iPhone it does not work. On both phones I'm logged into the same Game-Center account, I could confirm this by comparing the playerId in the GKLocalPlayer instance.

I've set up the proper iCloud container and linked my game to it, but the logs in the iCloud container backend remain empty.

What is going on? How can I share the saved game across Apple devices?

like image 549
Martin Braun Avatar asked Oct 24 '18 17:10

Martin Braun


People also ask

Can I transfer my game progress to another device?

Google Play Games has its own cloud-save method, but not all games utilize it. Nevertheless, it's worth setting up in case your game does support it. In order to sync your game progress between devices using Google Play Games, you'll need to be signed into the same Google account on both devices.

How do I sync games across devices?

Sign In to Google Play Games Services using desired Gmail ID. 2. Launch the game in order to Sync current game progress with the Google Play Google Services ID. To Sync game progress across multiple Android devices, you need to Log-In to all devices using the same Google Play Google Services ID and then play the game.

Can I have save data on 2 switches?

For the most convenient way of syncing game saves across two Switches, you'll need to be subscribed to Nintendo's online service and have cloud saves enabled.


1 Answers

The above sample in the question works just fine. It's mandatory that the user logs into iCloud and uses the same apple ID on the Game Center login, because the saved games will be stored in the iCloud.

Unfortunately, I was testing the whole without iCloud, so it couldn't work.

like image 121
Martin Braun Avatar answered Nov 15 '22 06:11

Martin Braun