I’m trying to get my game to allow 1 device (iPhone) to use GameCenter to invite a friend to play (iPad). I’m using the standard/default MatchMaker interface. The iPhone sends the invitation to the iPad which presents a notification.
When I press this notification the iPad’s ‘player(GKPlayer, didAccept: GKInvite)’ routine DOES get called.
#objc func player(_ playerMe: GKPlayer, didAccept invite: GKInvite) {
print("\n\n\t\tplayer \(playerMe.displayName)(\(playerMe.playerID)) did accept INVITE sent by \(invite.sender.displayName)(\(invite.sender.playerID))")
GKMatchmaker.shared().match(for: invite, completionHandler: {(InvitedMatch, error) in
print("\t\tplayers.count = \(InvitedMatch!.players.count)")
if error != nil {
print("INVITE ERROR: \(error.debugDescription)")
}
if InvitedMatch != nil {
print("\t\tSetting current match. (\(InvitedMatch.debugDescription))")
self.currentMatch = InvitedMatch
self.currentMatch?.delegate = self
// self.prepareMatch()
}
})
}
Output:
player Me(G:25139341913) did accept INVITE sent by “-----”(G:12453976)
players.count = 0
Setting current match. (Optional(<GKMatch 0x282d39970 expected count: 1 seqnum: 0
G:12453976:unknown
reinvitedPlayers:(
)>))
The players array is EMPTY! Shouldn’t it at least have the inviter in there? The ‘expectedPlayerCount’ properly reflects 2 person matchRequest where 1 player (the inviter) is already a participant)
At no point was the ‘player(GKPlayer, didRequestMatchWithRecipients: [GKPlayer])’ called by either end.
So the iPad doesn’t have access to the players to setup the match, but the iPhone sees that the invitation was accepted, has 2 players, and moves on. The iPhone code:
func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFind match: GKMatch) {
print("\n\n\t\tMATCH FOUND\n\n")
viewController.dismiss(animated: true, completion: nil)
GKMatchmaker.shared().stopBrowsingForNearbyPlayers()
currentMatch = match
match.delegate = self
if Globals.gameState?.currentState is StateWaitingForMatch {
if currentMatch?.expectedPlayerCount == 0 {
prepareMatch()
}
}
}
So how do I get the iPad (recipient of the invitation) to see/include the players?
In the player(GKPlayer, invite: GKInvite) method create a GKMatchMakerViewController via
let mmvc = GKMatchmakerViewController(invite: invite!)
and then present it:
viewController.present(mmvc!, animated: true, completion: nil)
Related
I am facing the issue of Multiplayer Invitation endless Processing below is the detail.
open func player(_ player: GKPlayer, didAccept inviteToAccept: GKInvite) {
print("player is \(player) \nInvite is \(inviteToAccept)")
EGC.guestPlayerIdentifier = player.playerID
guard let gkmv = GKMatchmakerViewController(invite: inviteToAccept) else {
EGCError.error("GKMatchmakerViewController invite to accept nil").errorCall()
return
}
gkmv.matchmakerDelegate = self
var delegeteParent:UIViewController? = EGC.delegate.parent
if delegeteParent == nil {
delegeteParent = EGC.delegate
}
delegeteParent!.present(gkmv, animated: true, completion: nil)
}
On average only 1 out of 5 tries would they be able to connect & play a
full match.
If one of them invited another who had the game open on his phone
he would get the invite and game would start but for the other, the game
would be blank or endlessly processing. However, if the invited didn't
have his game opened already it seemed to work more often. any help would be appreciated
.
In my iOS turn based match, I'm trying to receive notifications and to get the
public func player(_ player: GKPlayer, receivedTurnEventFor match: GKTurnBasedMatch, didBecomeActive: Bool)
to be called, with no success.
I register my view model to the local player
GKLocalPlayer.localPlayer().register(self)
and I would expect that to fire after the other player executes
func endTurn(withNextParticipants nextParticipants: [GKTurnBasedParticipant], turnTimeout timeout: TimeInterval, match matchData: Data, completionHandler: ((Error?) -> Swift.Void)? = nil)
but no success.
If I force a reload of the matchData then I will get the data the second player just submitted. So the endTurn works correctly.
Is there something I'm doing wrong?
Update:
So I create a new project, copied all my files over,
in the capabilities only Game Center was enabled.
When developing it was working perfect, I had two devices attached (with different apple IDs). Notifications were working and Turnbasedlistener was firing.
As soon as I released it for internal testing it stopped working!!!
I had very similar issue. My solution was to manually recheck my status while waiting for my turn.
FIrst, I defined global variable var gcBugTimer: Timer
In endTurn(withNextParticipants:turnTimeOut:match:completionHandler:) completion handler:
let interval = 5.0
self.gcBugTimer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(self.isMatchActive), userInfo: nil, repeats: true)
self.gcBugTimer.tolerance = 1.0
Code above also should be called in case when a player is joying to a new match and other player in a turn.
Then timer method:
func isMatchActive() {
// currentMatch - global variable contains information about current match
GKTurnBasedMatch.load(withID: currentMatch.matchID!) { (match, error) in
if match != nil {
let participant = match?.currentParticipant
let localPlayer = GKLocalPlayer.localPlayer()
if localPlayer.playerID == participant?.player?.playerID {
self.player(localPlayer, receivedTurnEventFor: match!, didBecomeActive: false)
}
} else {
print(error?.localizedDescription ?? "")
}
}
}
And I add following code at the very beginning of player(_:receivedTurnEventFor:didBecomeActive):
if gcBugTimer != nil && gcBugTimer.isValid {
gcBugTimer.invalidate()
}
What ended up working for me, was to test on an actual device, rather than in simulator. The receivedTurnEvents function doesn't seem to work in simulator.
Grigory's work around is great for testing with simulator.
I made a TurnBased game that was working perfectly with swift 2.
I could successfully change the code to swift 3.2 and correct all the errors about 400, but there is one issue with the game centre that I can not figure out how to fix it.
When through gamecenter viewcontroler I set the number of the participant to “2Player” and then push “Invite Friends” the number of Player that I get is 4 instead of 2!
the gamecenter send a text message to the next participant in which the number of participant is 4 too.
This of course affect the rest of the program a problem that I didn’t have in Swift 2.
This is part of my code:
func joinTurnBasedMatch()
{
let request = GKMatchRequest()
request.minPlayers = 2
request.maxPlayers = 4
request.defaultNumberOfPlayers = 2
let tbvc = GKTurnBasedMatchmakerViewController(matchRequest: request)
tbvc.turnBasedMatchmakerDelegate = self
present(tbvc, animated: true, completion: nil)
}
func turnBasedMatchmakerViewController(_ viewController: GKTurnBasedMatchmakerViewController, didFind match: GKTurnBasedMatch)
{
self.dismiss(animated: true, completion: nil)
self.performSegue(withIdentifier: "To_ViewC_CG", sender: match)
print("*** Number OF Players ***")
print(match.participants!.count)
}
Do I need to add anything to my code or change anything in it?
The problem has been solved in the latest ios update.
Now it works perfectly.
When switching to GKMatchmaker from MatchmakerViewController my two sandbox game center accounts running on separate devices (both iPhone 5s) no longer connect into the same match. Both show expectedPlayerCount still above 0.
I followed the RayWenderlich tutorial on creating a multiplayer game with game center. It is supposed to be real time and was working as such. Using a GKMatchmakerViewController it all worked completely fine (both auto-matching and invites). However, as soon as I switched one block of code it no longer works. I've been fiddling with this for awhile now and the apple docs don't seem to help me at all. Anyone know what I might need to do extra?
func findMatchWithMinPlayers(minPlayers: Int, maxPlayers: Int, viewController: UIViewController, delegate: GameKitHelperDelegate) {
if enableGameCenter == false {
return
}
matchStarted = false
gkMatch = nil
gkDelegate = delegate
viewController.dismissViewControllerAnimated(false, completion: nil)
var request = GKMatchRequest()
request.minPlayers = minPlayers
request.maxPlayers = maxPlayers
if pendingRecipients != nil {
request.recipients = pendingRecipients!
}
GKMatchmaker.sharedMatchmaker().findMatchForRequest(request, withCompletionHandler: { (match: GKMatch!, error: NSError!) -> Void in
self.gkMatch = match
self.gkMatch?.delegate = self
println(match)
if self.matchStarted == false && match.expectedPlayerCount == 0 {
self.lookupPlayers()
}
})
}
This only difference between this and what was working is that this has the GKMatchmaker instead of the GKMatchmakerViewController and Delegate.
I'm currently making a 2-player strategy board game in Swift and need to connect two iPads over local WiFi or Bluetooth. No matter what I've tried today, I can't get them to detect each other (I've tried over local WiFi and Bluetooth).
Here is my authorization code which runs in the UIViewController when my app first launches (which always returns "Self local player is authenticated." along with the ID:
private func authenticateLocalPlayer() {
var localPlayer = getLocalPlayer()
// If Apple were doing their job right, this is what the proper code should look like:
// var localPlayer = GKLocalPlayer.localPlayer()
if ( !localPlayer.authenticated ) {
localPlayer.authenticateHandler = { (viewController : UIViewController!, error : NSError!) -> Void in
NSLog("Error: \(error)")
if viewController != nil {
// Authenticated?
self.presentViewController(viewController, animated: true, completion: nil)
NSLog("viewController is not nil")
} else if (localPlayer.authenticated == true) {
NSLog("Self local player is authenticated.")
NSLog("My name is \(localPlayer.playerID)")
} else {
NSLog("Not authenticated")
NSLog("Player is \(localPlayer.playerID)")
}
}
} else {
NSLog("Player is already authenticated!")
}
}
and here is my code to detect nearby devices in a separate UIViewController:
override func viewDidLoad() {
devicesLabel.text = "Waiting for devices..."
searchForDevices()
NSLog("Ran searchForDevices()")
}
private func searchForDevices() {
GKMatchmaker.sharedMatchmaker().startBrowsingForNearbyPlayersWithHandler() {
var status = $1 ? "true" : "false"
self.devicesLabel.text = "Reachability changed for player \($0) with status: \(status)"
}
}
No matter what I do with my two iPads (both are model iPad 3), neither one ever sees the other. Am I calling startBrowsingForNearbyPlayersWithHandler correctly?
Also notice that in the authorization code above, I'm using the Objective-C workaround recommended by this post: Game Center not authenticating using Swift, since the "Swift way" of doing that didn't work for me either.
I also ran Spelltower across both devices over local WiFi, so it looks like the hardware is functioning properly. Any idea what could be going wrong here?
I decided to abandon developing this through Game Center and to use Multipeer Connectivity instead.
You are not registering a class to receive invitation updates. You need to register a class and implement methods conforming to the protocol for GKLocalPlayerListener. See my response in this post (it is in Objective-C, but the same concept applies):
Some startBrowsingForNearbyPlayersWithReachableHandler questions