Can I program a hold point into an Xbox game that waits for a user's cloud data sync to complete?

118 views Asked by At

Is it possible to program a startup hold into an Xbox game package that waits for the user's cloud saves to sync before continuing? I have an HTML5 game package for which I require save data to be loaded from Xbox Live which is then read from local storage (in JSON format) to populate the load screen at startup, and I also have to account for any error messages for which the user may have to respond. The game itself is started up once sync is done (but obviously not until after the legal stuff has been taken care of, so logo splash, engine branding, seizure advisory and possibly also the FBI and ESRB notices also have to be taken into account). If it also helps to investigate, data is being saved properly while there's an active session and I can successfully copy it to local app storage. The Xbox Live copy, on the other hand, seems to be at issue right now.

I'm not holding that much data in the game files, either - just five save slots not including global config and user settings. Basically I'm trying to keep the per-user storage well under the 64MB cap on creator's projects (not to mention that my initial request for ID@Xbox didn't work out) and I'm hoping to have this implemented before I even dare to resubmit. This is provided that it's even possible of course, considering all of the conflicting information I've been receiving through my Bing and Google requests.

The closest I have come is with the following code, which is based largely on Microsoft Docs samples. This first block is what's supposed to load the Xbox data and copy it to disk, and for which I require the delay in the first place:

    public void doStartup()
    {
        getData(-1);
        for (int i = 0; i <= 5; i++)
        {
            getData(i);
        }
    }
    public async void getData(int savefileId)
    {
        var users = await Windows.System.User.FindAllAsync();

        string c_saveBlobName = "Advent";
        //string c_saveContainerDisplayName = "GameSave";
        string c_saveContainerName = "file" + savefileId;
        if (savefileId <= 0) c_saveContainerName = "config";
        if (savefileId == 0) c_saveContainerName = "global";
        GameSaveProvider gameSaveProvider;

        GameSaveProviderGetResult gameSaveTask = await GameSaveProvider.GetForUserAsync(users[0], "00000000-0000-0000-0000-00006d0be05f");
        //Parameters
        //Windows.System.User user
        //string SCID

        if (gameSaveTask.Status == GameSaveErrorStatus.Ok)
        {
            gameSaveProvider = gameSaveTask.Value;
        }
        else
        {
            return;
            //throw new Exception("Game Save Provider Initialization failed");;
        }

        //Now you have a GameSaveProvider
        //Next you need to call CreateContainer to get a GameSaveContainer

        GameSaveContainer gameSaveContainer = gameSaveProvider.CreateContainer(c_saveContainerName);
        //Parameter
        //string name (name of the GameSaveContainer Created)

        //form an array of strings containing the blob names you would like to read.
        string[] blobsToRead = new string[] { c_saveBlobName };

        // GetAsync allocates a new Dictionary to hold the retrieved data. You can also use ReadAsync
        // to provide your own preallocated Dictionary.
        GameSaveBlobGetResult result = await gameSaveContainer.GetAsync(blobsToRead);

        string loadedData = "";

        //Check status to make sure data was read from the container
        if (result.Status == GameSaveErrorStatus.Ok)
        {
            //prepare a buffer to receive blob
            IBuffer loadedBuffer;

            //retrieve the named blob from the GetAsync result, place it in loaded buffer.
            result.Value.TryGetValue(c_saveBlobName, out loadedBuffer);

            if (loadedBuffer == null)
            {

                //throw new Exception(String.Format("Didn't find expected blob \"{0}\" in the loaded data.", c_saveBlobName));

            }
            DataReader reader = DataReader.FromBuffer(loadedBuffer);
            loadedData = reader.ReadString(loadedBuffer.Length);
            if (savefileId <= 0)
            {
                try
                {
                    System.IO.File.WriteAllText(ApplicationData.Current.LocalFolder.Path + "\\config.json", loadedData);
                }
                catch { }
            }
            else if (savefileId == 0)
            {
                try
                {
                    System.IO.File.WriteAllText(ApplicationData.Current.LocalFolder.Path + "\\global.json", loadedData);
                }
                catch { }
            }
            else
            {
                try
                {
                    System.IO.File.WriteAllText(ApplicationData.Current.LocalFolder.Path + "\\file"+savefileId+".json", loadedData);
                }
                catch { }

            }

        }
    }

And this is what's supposed to read the data when called by the HTML5 portion:

    public string getSaveFile(int savefileId)
    {
        string data;
        if (savefileId == 0)
        {
            try
            {
                data = System.IO.File.ReadAllText(ApplicationData.Current.LocalFolder.Path + "\\global.json");
                if (data == null) data = "";
                Debug.WriteLine(data);
            }
            catch { data = ""; }
            return data;
        } else
        {
            try
            {
                data = System.IO.File.ReadAllText(ApplicationData.Current.LocalFolder.Path + "\\file" + savefileId + ".json");
                if (data == null) data = "";
                Debug.WriteLine(data);
            }
            catch { data = ""; }
            return data;
        }
    }
    public string getConfig()
    {
        string data;
        try
        {
            data = System.IO.File.ReadAllText(ApplicationData.Current.LocalFolder.Path + "\\config.json");
            if (data == null) data = "";
            Debug.WriteLine(data);
        }
        catch { data = ""; }
        return data;
    }

And this is the save code on the WinRT side:

    public async void doSave(int key, string data)
    {
        //Get The User
        var users = await Windows.System.User.FindAllAsync();

        string c_saveBlobName = "Advent";
        string c_saveContainerDisplayName = "GameSave";
        string c_saveContainerName = "file"+key;
        if (key == -1) c_saveContainerName = "config";
        if (key == 0) c_saveContainerName = "global";
        GameSaveProvider gameSaveProvider;

        GameSaveProviderGetResult gameSaveTask = await GameSaveProvider.GetForUserAsync(users[0], "00000000-0000-0000-0000-00006d0be05f");
        //Parameters
        //Windows.System.User user
        //string SCID

        if (gameSaveTask.Status == GameSaveErrorStatus.Ok)
        {
            gameSaveProvider = gameSaveTask.Value;
        }
        else
        {
            return;
            //throw new Exception("Game Save Provider Initialization failed");
        }

        //Now you have a GameSaveProvider (formerly ConnectedStorageSpace)
        //Next you need to call CreateContainer to get a GameSaveContainer (formerly ConnectedStorageContainer)

        GameSaveContainer gameSaveContainer = gameSaveProvider.CreateContainer(c_saveContainerName); // this will create a new named game save container with the name = to the input name
                                                                                                     //Parameter
                                                                                                     //string name

        // To store a value in the container, it needs to be written into a buffer, then stored with
        // a blob name in a Dictionary.

        DataWriter writer = new DataWriter();

        writer.WriteString(data); //some number you want to save, in this case 23.

        IBuffer dataBuffer = writer.DetachBuffer();

        var blobsToWrite = new Dictionary<string, IBuffer>();

        blobsToWrite.Add(c_saveBlobName, dataBuffer);

        GameSaveOperationResult gameSaveOperationResult = await gameSaveContainer.SubmitUpdatesAsync(blobsToWrite, null, c_saveContainerDisplayName);
        int i;
        for (i = 1; i <= 90000; i++) {}
        Debug.WriteLine("SaveProcessed");
        //IReadOnlyDictionary<String, IBuffer> blobsToWrite
        //IEnumerable<string> blobsToDelete
        //string displayName        
    }

And I have just recently added the user detection code to the main project.

    public static async void InitializeXboxGamer(TextBlock gamerTagTextBlock)
    {
        try
        {
            XboxLiveUser user = new XboxLiveUser();
            SignInResult result = await user.SignInSilentlyAsync(Window.Current.Dispatcher);
            if (result.Status == SignInStatus.UserInteractionRequired)
            {
                result = await user.SignInAsync(Window.Current.Dispatcher);
            }
            System.IO.File.WriteAllText(ApplicationData.Current.LocalFolder.Path + "\\curUser.txt", user.Gamertag);
        }
        catch (Exception ex)
        {
            // TODO: log an error here
        }
    }
1

There are 1 answers

0
Jeffrey Davis On BEST ANSWER

Big dumb... I figured out the problem with my code. When loading the global config, getData is passed a negative one; however for some reason it was writing the app data file instead so after I changed the verification to be a little more specific it started to work as expected.

    public void doStartup()
{
    getData(-1);
    for (int i = 0; i <= 5; i++)
    {
        getData(i);
    }
}
public async void getData(int savefileId)
{
    var users = await Windows.System.User.FindAllAsync();

    string c_saveBlobName = "Advent";
    //string c_saveContainerDisplayName = "GameSave";
    string c_saveContainerName = "file" + savefileId;
    if (savefileId <= 0) c_saveContainerName = "config";
    if (savefileId == 0) c_saveContainerName = "global";
    GameSaveProvider gameSaveProvider;

    GameSaveProviderGetResult gameSaveTask = await GameSaveProvider.GetForUserAsync(users[0], "00000000-0000-0000-0000-00006d0be05f");
    //Parameters
    //Windows.System.User user
    //string SCID

    if (gameSaveTask.Status == GameSaveErrorStatus.Ok)
    {
        gameSaveProvider = gameSaveTask.Value;
    }
    else
    {
        return;
        //throw new Exception("Game Save Provider Initialization failed");;
    }

    //Now you have a GameSaveProvider
    //Next you need to call CreateContainer to get a GameSaveContainer

    GameSaveContainer gameSaveContainer = gameSaveProvider.CreateContainer(c_saveContainerName);
    //Parameter
    //string name (name of the GameSaveContainer Created)

    //form an array of strings containing the blob names you would like to read.
    string[] blobsToRead = new string[] { c_saveBlobName };

    // GetAsync allocates a new Dictionary to hold the retrieved data. You can also use ReadAsync
    // to provide your own preallocated Dictionary.
    GameSaveBlobGetResult result = await gameSaveContainer.GetAsync(blobsToRead);

    string loadedData = "";

    //Check status to make sure data was read from the container
    if (result.Status == GameSaveErrorStatus.Ok)
    {
        //prepare a buffer to receive blob
        IBuffer loadedBuffer;

        //retrieve the named blob from the GetAsync result, place it in loaded buffer.
        result.Value.TryGetValue(c_saveBlobName, out loadedBuffer);

        if (loadedBuffer == null)
        {

            //throw new Exception(String.Format("Didn't find expected blob \"{0}\" in the loaded data.", c_saveBlobName));

        }
        DataReader reader = DataReader.FromBuffer(loadedBuffer);
        loadedData = reader.ReadString(loadedBuffer.Length);
        if (savefileId <= 0)
        {
            try
            {
                System.IO.File.WriteAllText(ApplicationData.Current.LocalFolder.Path + "\\config.json", loadedData);
            }
            catch { }
        }
        else if (savefileId == 0)
        {
            try
            {
                System.IO.File.WriteAllText(ApplicationData.Current.LocalFolder.Path + "\\global.json", loadedData);
            }
            catch { }
        }
        else
        {
            try
            {
                System.IO.File.WriteAllText(ApplicationData.Current.LocalFolder.Path + "\\file"+savefileId+".json", loadedData);
            }
            catch { }

        }

    }
}

I still need the execution delay though, but that's for an entirely different reason that I discuss over here.