endTurnWithNextParticipants doesn't trigger receivedTurnEventForMatch after update to iOS 8.3 and swift 1.2
Asked Answered
M

4

10

Has anyone noticed any changes to turn based match notifications since updating to iOS8.3? In my app when I call endTurnWithNextParticipants, prior to the upgrade, this was resulting in a notification being sent to the opponent which would trigger a receivedTurnEventForMatch being invoked on their device but this no longer is the case. When the opponent terminates the app and restarts it they can see it is their turn so the match in game centre was correctly updated with the order of participation but this doesn't seem to take effect dynamically any more.

Anybody else see this? I'm hoping it's just a temporary glitch in game centre sandbox environment.

I have raised a bug report with apple to see if it actually is a bug or if there is some undocumented behavioural changes in iOS8.3 we need to know about.

Mullins answered 10/4, 2015 at 19:3 Comment(8)
same here. this is a serious bug, should we now reload all matches continuously to check for new matches? hope this will be fixed soon. Please update the question if you have any newsDentation
I'm in the same boat here. Completely breaks my game. To prevent this from being just another "me too" post, I have one additional observation: if you execute saveCurrentTurnWithMatchData: the other player DOES still receive a notice.Polo
Yes, I can confirm the saveCurrentTurnWithMatchData does trigger the notification.Mullins
Having the same problem. Really glad to find this post.Dastard
Same problem. I want to add, for me this isn't consistently an issue. It does only happen with devices on 8.3, but it's not happening for everyone.Scabrous
Anyone know if this has been addressed in 8.4?Dastard
Not sure. All I know is I'm not the only one to have raised this as a bug with apple.Mullins
FWIW (aka not much) my bug was closed as a dup. I guess that at least that's an acknowledgement. :/Polo
X
3

Update: Apple has responded to the bug report:

Please verify this issue with iOS 8.4 beta 4 (Build: 12H4125a) and update your bug report at http://bugreport.apple.com/ with your results.

iOS 8.4 beta 4 (Build: 12H4125a) https://developer.apple.com/ios/download/ Posted: June 9th, 2015

Unfortunately I am not able to install iOS 8.4 beta 4 on my devices and can't tell you whether it's fixed now. If anybody of you has this opportunity, please share.


I have submitted a bug report to Apple regarding this issue and will post updates here when they have responded.

My turn-based game has this workaround. Looks terrible, but it's working again – maybe it helps you.

- (void)sendGameStateWith:(NSMutableDictionary *)data
{
    if (self.match) {
        NSData *matchdata = [NSKeyedArchiver archivedDataWithRootObject:data];

        GKTurnBasedParticipant *opponent = [self.match.participants objectAtIndex:0];
        GKTurnBasedParticipant *localPlayer = [self.match.participants objectAtIndex:1];
        if ([self.localPlayer.playerID isEqualToString:opponent.playerID]) {
            opponent = [self.match.participants objectAtIndex:1];
            localPlayer = [self.match.participants objectAtIndex:0];
        }

        // HINT: Remove this workaround when Apple has fixed it. 
        [self.match saveCurrentTurnWithMatchData:matchdata completionHandler:^(NSError *error) {
            if (error) {
                NSLog(@"Error on saveCurrentTurnWithMatchData");
                [self sendGameStateWith:data];

            } else {
                [self.match endTurnWithNextParticipants:[NSArray arrayWithObjects:opponent, localPlayer, nil] turnTimeout:turnTimeout matchData:matchdata completionHandler:^(NSError *error) {
                    if (error) {
                        NSLog(@"Error: Send Game Center state");
                    } else {
                        NSLog(@"Sent Game Center state");
                    }
                }];
            }
        }];
    }
}
Xenocrates answered 3/5, 2015 at 12:20 Comment(4)
@Appsunited: I think you're relying on the two actions to fire ~immediately, so that by the time the receiver gets the (still working) event for the saveCurrentTurn they will already be the active participant because of the endTurn, correct? In my testing, I ran into trouble with delays on the game center end between those two actions, so that the recipient was not the current participant when it received the first event. (hence, I started the timer loop on the receiver to wait for the endTurn). Has the timing been reliable for you?Polo
I'm seeing the same thing as @Polo is reporting with this method: receivedTurnEvenForMatch is called, but currentParticipant is still the participant whose turn just ended.Gossett
I also confirm, that my workaround code is not working as I hoped. Here is another workaround: The local player has an open match and is waiting for an end turn by the opponent, the game is loading that match every 1-3 seconds and updates the match, if the opponent has responded and new matchData is loaded. It's also a hack and an oldschool technique, but it's working reliably so far.Xenocrates
iOS 8.4 is out now. I have tested it and it's working again. You don't need any workaround anymore. :-)Xenocrates
P
4

Updated answer:

My original idea, below, turns out to not be reliable. You cannot count on the saveCurrentTurnWithMatchData to send timely notifications. It works most of the time, but at least 10% of the time, it fails to send a notifications as well. Of all the ideas posted here, the only thing I have found that works 100% reliably is to set up a timer loop on the non-active machines and continuously watch until you become active.

-(void)isMatchActive:(NSTimer *)timer
{
    NSString *matchID = (NSString *)timer.userInfo;

    [GKTurnBasedMatch loadMatchWithID:matchID withCompletionHandler:^(GKTurnBasedMatch *match, NSError *error)
    {
        GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
        GKTurnBasedParticipant *currentParticipant = match.currentParticipant;

        if ([localPlayer.playerID isEqualToString:currentParticipant.player.playerID])
        {
            //we have become active. Call the event handler like it's supposed to be called
            [self player:localPlayer receivedTurnEventForMatch:match didBecomeActive:false];
        }
        else
        {
            //we are still waiting to become active. Check back soon
            float dTime = 5.0;
            gameController.IOS8BugTimer = [NSTimer scheduledTimerWithTimeInterval:dTime
                                                                          target:self
                                                                     selector:@selector(isMatchActive:)
                                                                     userInfo:matchID
                                                                      repeats:NO];
         }
     }];
}

Original Answer:

So, I have a work around that is kludgy but looks promising. Note that, per my comment above, the subsequent players still receive events after the current player executes saveCurrentTurnWithMatchData. So I use that send my own custom signal:

  1. I added a string to my matchData for the "next player's ID."

  2. Right before I call endTurnWithNextParticipants, I set that string to the ID of the next player in the rotation.

  3. I call saveCurrentTurnWithMatchData

  4. I moved the call to endTurnWithNextParticipants inside saveCurrentTurnWithMatchData's completion handler, to ensure it doesn't fire until after saveCurrentTurnWithMatchData.

  5. For the purposes of this work around, I added the GKTurnBasedEventHandlerDelegate to my code. handleTurnEventForMatch is broken in 8.3 too, but I consolidated all of my workaround code there and didn't have to make changes to receivedTurnEventForMatch

  6. In handleTurnEventForMatch, I check to see if the "next player" string is me. If so, I assume that the current player just saved the game in step 2, signalling his intention to pass the turn to me.

  7. I start a timer loop, reloading the match data until it shows that I have become the active player.

  8. When I see that I am now the active player, I reset my custom next player string to nil and I manually call receivedTurnEventForMatch passing it the match data I just downloaded.

To summarize, player1 sends an extra saveTurn event to signal that he intends to end the turn. When player2 receives that signal, he re-reads the match data until it shows that he has become active. Then he calls his own receivedTurnEventForMatch with the updated match data, allowing the turn to proceed.

I haven't gotten through all my scenarios yet, but it looks like this is going to work.

Polo answered 11/4, 2015 at 23:33 Comment(0)
O
3

I have the same problem.. I have multiple devices, and device with 8.3 do not trigger receivedTurnEventForMatch event, but when is application in background then banner notification about turn will appear as usual. Problem is only in case when application is in foreground.

On the other hand, devices with 8.2 or 8.1.3 works well in any case. So this is certainly caused by 8.3. Also device with 8.3 is in Xcode marked as ineligible. Does it happen to you too?

Oldtimer answered 11/4, 2015 at 12:29 Comment(2)
Yes it does happen to me. I have to select the device manually via Product -> Destination -> <device>Mullins
By the way: I have tested it on iOS 8.3, 8.0 and 7.1.2. Only 7.1.2 did work correctly.Xenocrates
X
3

Update: Apple has responded to the bug report:

Please verify this issue with iOS 8.4 beta 4 (Build: 12H4125a) and update your bug report at http://bugreport.apple.com/ with your results.

iOS 8.4 beta 4 (Build: 12H4125a) https://developer.apple.com/ios/download/ Posted: June 9th, 2015

Unfortunately I am not able to install iOS 8.4 beta 4 on my devices and can't tell you whether it's fixed now. If anybody of you has this opportunity, please share.


I have submitted a bug report to Apple regarding this issue and will post updates here when they have responded.

My turn-based game has this workaround. Looks terrible, but it's working again – maybe it helps you.

- (void)sendGameStateWith:(NSMutableDictionary *)data
{
    if (self.match) {
        NSData *matchdata = [NSKeyedArchiver archivedDataWithRootObject:data];

        GKTurnBasedParticipant *opponent = [self.match.participants objectAtIndex:0];
        GKTurnBasedParticipant *localPlayer = [self.match.participants objectAtIndex:1];
        if ([self.localPlayer.playerID isEqualToString:opponent.playerID]) {
            opponent = [self.match.participants objectAtIndex:1];
            localPlayer = [self.match.participants objectAtIndex:0];
        }

        // HINT: Remove this workaround when Apple has fixed it. 
        [self.match saveCurrentTurnWithMatchData:matchdata completionHandler:^(NSError *error) {
            if (error) {
                NSLog(@"Error on saveCurrentTurnWithMatchData");
                [self sendGameStateWith:data];

            } else {
                [self.match endTurnWithNextParticipants:[NSArray arrayWithObjects:opponent, localPlayer, nil] turnTimeout:turnTimeout matchData:matchdata completionHandler:^(NSError *error) {
                    if (error) {
                        NSLog(@"Error: Send Game Center state");
                    } else {
                        NSLog(@"Sent Game Center state");
                    }
                }];
            }
        }];
    }
}
Xenocrates answered 3/5, 2015 at 12:20 Comment(4)
@Appsunited: I think you're relying on the two actions to fire ~immediately, so that by the time the receiver gets the (still working) event for the saveCurrentTurn they will already be the active participant because of the endTurn, correct? In my testing, I ran into trouble with delays on the game center end between those two actions, so that the recipient was not the current participant when it received the first event. (hence, I started the timer loop on the receiver to wait for the endTurn). Has the timing been reliable for you?Polo
I'm seeing the same thing as @Polo is reporting with this method: receivedTurnEvenForMatch is called, but currentParticipant is still the participant whose turn just ended.Gossett
I also confirm, that my workaround code is not working as I hoped. Here is another workaround: The local player has an open match and is waiting for an end turn by the opponent, the game is loading that match every 1-3 seconds and updates the match, if the opponent has responded and new matchData is loaded. It's also a hack and an oldschool technique, but it's working reliably so far.Xenocrates
iOS 8.4 is out now. I have tested it and it's working again. You don't need any workaround anymore. :-)Xenocrates
F
2

We had the same issue under iOS 8.3 and it has been fixed in yesterdays release of iOS 8.4.

UPDATE 1

in our case the workaround of @appsunited did solve it for iOS 8.3 and is not necessary anymore with iOS 8.4.

we tested both sandbox and our current app store version. and played with an iPad 3 with iOS 7.1.2 against another iPad 3 with iOS 8.3 and yesterday 8.4.

Futuristic answered 1/7, 2015 at 12:44 Comment(4)
That's good to know. Thanks! At least it confirms apple acknowledged they broke it.Mullins
Pinxaton, sorry to hear that. 8.4 is working properly again for me.Polo
It might be something I'm doing wrong. will look into it.Mullins
I've still got this problem. Various forms of appsunited's solution sort of work, but not well. The game play takes turns for a while and then hangs. The player that should receive -receivedTurnEventForMatch doesn't get the notification even though Game Center is showing that it's his turn. Taking the turn in Game Center resumes the game correctly. I'm using an iPadMini and iPadAir2, both running 8.4.Fonteyn

© 2022 - 2024 — McMap. All rights reserved.