In which method methods should game state be saved/loaded - save

There are a lot of articles about how to save a state of a game and they are pretty good. But I have one conceptual misunderstanding where should I save the state?
My libGDX game has number of screens and pair of them are MainMenuScreen and MainSceneScreen these are inherited from Screen class. MainMenuScreen is shown at start of the game the MainSceneScreen little later.
What is the problem? I navigated to MainSceneScreen, forced Android to stop the application (I change a language settings on the device to achieve this). After that I select the application again and I can see MainMenuScreen is shown. But I want MainSceneScreen to be shown.
I suppose I should override resume method. But which one? I have class PsGame that extends Game class of libgdx. I put breakpoints to its resume method and it turned out that method was not called. I investigated the problem and I've found this strange code in the onResume method of AndroidApplication class in libGDX:
if (!firstResume)
graphics.resume();
else
firstResume = false;
My debugger said firstResume was true and didn't go to graphics.resume() line.
What did I do wrong?
What methods should I override?

I think you've narrowed this down correctly, and you should be restoring your saved state during the resume method call on your Game subclass. This should be called if you switch to some other app for a bit, and then switch back to your app (without going through the steps that will cause the app to be terminated). For whatever reason, libgdx does not forward the resume call for the initial startup (this design is probably a good idea in the long run, but is a bit confusing when starting out). Its easy enough to invoke your restore code from the create method in your Game (which is only invoked at application startup) to get the first start of your game to have 'resume' behavior.

Related

Why is runloop called when testing

I usually see RunLoop.current.run(until: Date()) called in integration tests.
For example in this article and in this open source project.
The explanation given in the article is
The RunLoop.current.run(until: Date()) statement makes sure the run loop associated with the current thread has ample time to let the drawing operations to complete
If that is true why wouldn't we give it more time, how do we know that Date() is enough?
I read a few articles about the run loop and it seems to me like the reason that line of code is added is to start the app. It seems like the appDelegate usually automatically triggers or starts the run loop, but since we are testing we need to trigger the run loop ourselves.
I might be missing some fundamental understand of threads or run loops but I hope that someone can give some insight.
If that is true why wouldn't we give it more time, how do we know that Date() is enough?
We know from experimentation, I suppose. A more “correct” way to do it would be:
Install a run loop observer that somehow detects when the views have been laid out and drawn.
Run the run loop indefinitely (until Date.distantFuture).
In the observer installed in step 1, call CFRunLoopStop(CFRunLoopGetMain()) when it detects that the views have been laid out and drawn.
However, that's a lot of extra work to do instead of just RunLoop.current.run(until: Date()), when the simpler method works and is unlikely to break.
I read a few articles about the run loop and it seems to me like the reason that line of code is added is to start the app. It seems like the appDelegate usually automatically triggers or starts the run loop, but since we are testing we need to trigger the run loop ourselves.
No, the app delegate does not start the run loop. The function UIApplicationMain sets up the app and starts the run loop. If you create a new Objective-C project, you will find that the main function in main.m calls UIApplicationMain. In a typical Swift project, the #UIApplicationMain attribute attached to the app delegate tells the Swift compiler to generate equivalent code (a main function that calls UIApplicationMain) in AppDelegate.o.
I might be missing some fundamental understand of threads or run loops but I hope that someone can give some insight.
An app spends most of its life in the run loop, which (simplified) has these phases:
Wait for an event. There are lots of event sources, including app startup, touches, timers, accelerometer, GPS, going to the background, returning to the foreground, and more.
Handle the event, often by calling code written by you.
If any views have the needsLayout property set, lay out the appropriate sections of the view hierarchy (which includes sending layoutSubviews messages).
If any views have the needsDisplay property set, draw those views (by sending drawRect: messages).
Go back to step 1.

Where to put code that gets checked frequently?

I have some code that needs to get called frequently, such as check what day it is, if it's the next day then move the day strings in the tableView.
Now I thought that the viewDidLoad would get called all the time and so it would be 'fine' to put it in there. However, I've left the simulator overnight, and I've pressed the home button and clicked again, changed VCs etc. and viewDidLoad hasn't been hit.
What are my options for doing sporadic checks such as, is it a new day? As x happened etc.
In this specific case, you can subscribe to NSCalendarDayChangedNotification to be notified when the date changes and respond accordingly in your view controller. In general, didBecomeActive or viewDidAppear would likely work.
What are my options for doing sporadic checks such as, is it a new day
It depends what the meaning of "is" is! In particular, "is" when? You say "sporadic", but that's just fluff. When do you need to know this? To what stimulus do you want to respond? When the user opens your app? Then put it in applicationDidBecomeActive. Every day at noon? Then run an NSTimer. Really, the problem here is that you don't seem to know, yourself, just when you need to perform these checks.
Whilst in your app, its quite easy to continually check for something. You simply create a background thread. However, what you describe is a thread that persists from outside the app's lifecycle.
Have a read on this documentation provided by Apple itself. You have to have good excuse to put a background thread. The scope of such thread is limited to only certain scenarios such as downloading background stuff, playing sounds etc.
For your scenario, I'd look at applicationDidBecomeActive(_:) found in your Application Delegate. There you can mimic such continual check. Beware however, don't put heavy word load on start up or your app might be killed automatically if it fails to become active in reasonable amount of time.

Re-Call appdelegate applicationdidfinishload

Inside my code, there's a place that I want to recall the applicationdidfinishload function, it's like restarting the app, a workaround since apple wouldn't like it if the app restarts.
But I fail to do it no matter what I try… Is there any way to do so? just reload the app by calling that function... but how to call it? I tried several methods from the internet but no luck...
I just want from within the app, in another view controller, if a certain condition I have is fulfilled, to reload the applicationDidFinishLoading from the app delegate.
That method is not responsible for loading the app. The system loads your app (using UIApplicationMain) and tells you when the app has been loaded using that delegate method, so that you can prepare for the app's normal execution (such as setting shared resources, UI elements, appearance, etc.).
UIApplicationMain will never return, under normal circumstances, thus you will not be allowed to restart the application.
If you want to restart your interface orientation, one easy solution is to throw away the UIWindow object and create a new one. Note, that this is a terrible user experience!
If you have come to an unspeakable situation where you are no longer able to continue executing the app (you should not get to such situations but the worst of cases), you should notify the user that he should kill the app from task manager and restart it themselves.

removeWithCompletionHandler causes logging out of Game Center

I am using GKTurnBasedMatch's removeWithCompletionHandler to programmatically remove old (finished, i.e., with status = GKTurnBasedMatchStatusEnded) turn-based matches from Game Center when needed (to avoid extra loading burden, I want to keep the amount of finished matches to a minimum).
I am getting no error and the match is correctly removed.
However, most times I do this, my user gets kicked out of Game Center, so the local player is no longer authenticated. This happens on iOS7, with both iPhone and iPad.
Did anyone experience this? Is there any way around it?
NB: I might as well not remove matches from GC, as I use internal business logic to determine which matches should be listed to the user and show, say, only the 10 (unfinished) most recent ones. However, I am afraid that hundreds of games might be kept in GC and that this might slow down interactions with GC when I have to list ongoing matches.
If you're calling removeWithCompletionHandler inside of a completion handler for another Game Center API call then you might be experiencing the very same issue that's been plaguing me for a while. The solution I tried today involves simply delaying the call to removeWithCompletionHandler for a few seconds, like so:
[match performSelector:#selector(removeWithCompletionHandler:) withObject:^(NSError *error) { /* callback code here */ } afterDelay:3.0];
For me, this worked great but meant I had to rework a few things in relation to displaying active games... so be wary of that if this solution works for you. Also, it's worth noting that the 3 second delay is an arbitrary value I picked and it worked for me.
In my case, I was calling removeWithCompletionHandler inside the completion handler block for the various quit methods on GKTurnBasedMatch.
I imagine there is some issue on Game Center's end where the two requests cannot be made so close together without resulting in such woe. This is evidenced by the fact that the issue did not ever occur for me when I slowly stepped through the calls to Game Center.
EDIT
Here's some cool news; seems like they've fixed the bug in iOS 8. I built an app which demonstrates the bug for bug-reporting purposes (originally for iOS 7). I've tested that same app running in iOS 8 and it seems that the bug has been squashed.
Finally.
IMO, you should consider adding the removeWithCompletionHandler call in now.

iOS 4.1 CLLocationManager delegate stops getting called - RunLoop blocked?

OK, this one has me stumped. I use the CLLocationManager services (iOS 4.1) in my application. The delegate gets called as it should at first. Then (some arbitrary time interval later), the delegate stops getting called. It almost looks like that RunLoop is getting blocked somewhere.
I have even reduced the callback to one NSLog statement, and I see the same behavior. Do we have any gdb experts out there who could give me some hints how to look at all of the running threads, and determine which one is blocked where ?
As a test, I have also put a button on the GUI which stops are starts both heading and location updates - this seems to unjam things for a while.
Other info:
This is on an iPhone 4, app has been run through Instruments (leaks and allocations), everything looks good there. Any hints would be appreciated, I am currently out of ideas...
Mea culpa... I had errant logic which was firing a timer, shutting off the service. One again, caution is called for when making supposed "small" changes. My apologies if anyone wasted effort on this one....

Resources