Xbox LIVE Indie Games
Sort Discussions: Previous Discussion Next Discussion
Page 1 of 1 (5 posts)

Startup storage problem

Last post 11/6/2008 5:05 PM by Third Party Ninjas. 4 replies.
  • 11/6/2008 3:24 PM

    Startup storage problem

    Note:  I'm making some assumptions based on my testing in this article.  If someone with a better understanding of such things reads this, please feel free to correct me.

    An interesting issue came up with my game during peer review, and while it was easy to fix, it was a pretty strange process to diagnose.  Since a few other games have been rejected lately for errors that look exactly the same, I thought I'd share.

    Description:
    Start the game with two or more storage devices and with the guide visible.  It will do some odd flickering, and then when everything seems to settle down and when you close the guide, the game crashes.

    Problem:
    If (like me) you're displaying the storage selection screen before doing anything else, checking Guide.IsVisible to prevent a GuideAlreadyVisible doesn't work like I would expect.  When starting the game with the guide open, you'll notice that it says you're being logged in and out of xbox live a couple of times pretty quickly, and the guide flickers, which seems to me that it's going between visible and not visible states quickly.  (This is likely not exactly what's happening, but it's obvious to me by watching it that there's some complicated stuff going on under the hood.  Also, since it does the same log in/out and flickering with XBLA titles, this doesn't seem to be a bug, just something we need to watch out for.)

    Example:
    Before describing how I got around the problem, let's see it in action.  (This is not the best way to handle storage.  It's a minimal example showing how the problem can happen.)

    1) Start a new Xbox 360 game project.

    2) Add the GamerServices component, since we need it for the storage stuff.  Add this to your constructor.
            Components.Add(new GamerServicesComponent(this));

    3) Add some variables to the class:
            bool storagePending = false;
            StorageDevice storageDevice = null;

    4) Add the code to start opening a storage device.  This goes in the Update method.
            if (!Guide.IsVisible && !storagePending && (storageDevice == null || !storageDevice.IsConnected))
            {
                storagePending = true;
                Guide.BeginShowStorageDeviceSelector(StorageCallback, null);
            }

    5)  And finally the storage callback.  Add this to your class:
            void StorageCallback(IAsyncResult result)
            {
                // note: I made an error in the first posting of this.  I've corrected it based on the suggestion Louis gave below.
                storageDevice = Guide.EndShowStorageDeviceSelector(result);
                // check for user cancelling the selection screen
                if(storageDevice != null && storageDevice.IsConnected == false)
                {
                    storageDevice = null;
                }
                storagePending = false;
            }

    Assuming I didn't miss anything in my write up of the steps, you should be able to build that and download to a 360.  Start the game, and immediately hit the guide button before it finishes launching.  You'll see the weird flickering and log in/out stuff, and then when you close the guide, you get a crash.

    Here's my analysis of what's happening:

    At some point during the flickering, Guide.IsVisible returns false.  We get into the if block, then it immediately goes back to being visible.  Then boom, we get an exception.

    ---->  Guide not visible
            if (!Guide.IsVisible)
            {
    ---->  Guide visible again
                Guide.BeginShowStorageDeviceSelector(StorageCallback, null);
    ---->  Exception thrown but not caught!
            }

    As I see it, we have two solutions.
    1) Move your storage selection code to a place that happens after all the start up stuff has resolved.  Nick Gravelyn's Bloc does this by waiting until the user presses start.  He doesn't process button presses if the guide is visible, so there's no way to get there if the guide is visible.  This is my recommended solution, if you can do it.

    2) Accept that this is a possibility and process any thrown exceptions.  For example, change the code we put in the Update method (step 4 above) to this:
            if (!Guide.IsVisible && !storagePending && (storageDevice == null || !storageDevice.IsConnected))
            {
                try
                {
                    storagePending = true;
                    Guide.BeginShowStorageDeviceSelector(StorageCallback, null);
                }
                catch
                {
                    storagePending = false;
                    storageDevice = null;
                }
            }

    I've done my best to keep this simple, but it's a complex topic.  Feel free to ask me any questions.  Please let me know if you see any errors in my write up or the assumptions I made in writing it.

  • 11/6/2008 3:58 PM In reply to

    Re: Startup storage problem

    I had some similar issues with my code, and I think the issue is simply that it is asynchronous. For example, with your code:
    void StorageCallback(IAsyncResult result)
    {
    storagePending = false;
    storageDevice = Guide.EndShowStorageDeviceSelector(result);
    // check for user cancelling the selection screen
    if(storageDevice != null && storageDevice.IsConnected == false)
    {
    storageDevice = null;
    }
    }


    The problem here is that storagePending is set to false and then
    if (!Guide.IsVisible && !storagePending && (storageDevice == null || !storageDevice.IsConnected))
    returns true before the Async Callback is complete. It is just a volatile variable problem. Moving the
    storagePending = false;
    line to the end of the callback should fix the problem.

    Alternatively, it would be nice to have a synchronous version that just locks up our program until it is finished.
  • 11/6/2008 4:05 PM In reply to

    Re: Startup storage problem

    Thanks for pointing that out.  I checked my real code, and I'm doing it properly, but I made a mistake in this quick and dirty example.  I'll edit the original post to fix that.
  • 11/6/2008 5:01 PM In reply to

    Re: Startup storage problem

    One method of waiting until things have "settled down" (as you call it), would be the following:

    Obviously, if the guide is opened already before game start, it flashes in the described fashion. And according to your interpretation (which seems plausible) while it flashes it seems to switch from IsVisible to !IsVisible and back a few times. Sometimes it even seems do to this in the middle of the game loop, so for the "If(Guide.IsVisible)" it's still "false", but for the "BeginShowStorageDeviceSelector" it's already "true" again (ergo the exception).

    Therefore, simply wait with the BeginShowStorageDeviceSelector until a second or so after game start - but, that must be a second during which the Guide was not visible!

    You could for example have a counter to which you add the elapsed time, but only if the guide is currently not visible:

    if (!Guide.IsVisible) {
       counter += gameTime.ElapsedGameTime.TotalSeconds;
       if (counter >= 1)
          Guide.BeginShowStorageDeviceSelector(...);
    }

    Even though the counter may also increase a little bit during the flashing of the guide, those short moments where the guide flashes "off" will in total certainly not amount to a full second. Probably only to a few milliseconds. Therefore, the counter does not increase far enough during the flashing, and BeginShowStorageDeviceSelector will not be called. Only once the guide is dismissed will the counter continue to be increased, so about a second later the call to BeginShowStorageDeviceSelector will happen.

    Now you might say that adding an additional second of wait time to the beginning of the game is probably not the best of ideas, and I would agree. :)

    But most games have a splash screen with the creator logo (or something similar) anyway, so use the time during which that screen is showing!

    Actually, this is the reason why my game is not affected by this problem (which is a coincidence, as I was not aware that the problem even existed):

    At the beginning of the game, I show my splash screen for a few seconds. At the same time I start loading the game data in parallel, on a second thread (at this time of course only data that is loaded from title storage, for example through the content pipeline). The call to BeginShowStorageDeviceSelector only happens after both the splash screen timeout is expired (a few seconds) and the initial loading from title storage is over (whichever takes longer).
    The timeout of the splash screen is however only updated if the guide is currently not visible (similar to the counter example of above). So if the guide is already open when the game starts, the splash screen timer will not advance (or only a very little bit, while the guide flashes a few times). Which means that the splash screen simply stays open while the guide is also open. Once the guide is closed, the splash screen timer will then expire sooner or later, and only then will the storage selector be opened.
    And if the guide is not open while the game is started, there will not be an additional delay, only the splash screen delay that would have been there anyway.

    Doc
  • 11/6/2008 5:05 PM In reply to

    Re: Startup storage problem

    Spyn Doctor:
    At the beginning of the game, I show my splash screen for a few seconds.


    This is my recommended solution, and what I'll be doing in all my future games.
Page 1 of 1 (5 posts) Previous Discussion Next Discussion