How can I tell if I'm the one who disconnected first in a GC match? - ios

My iOS game has Game Center multiplayer matches. They are 1v1 battles.
If a player disconnects (that is, their state changes to GKPlayerStateDisconnected), such player loses the duel automatically (even if they manage to reconnect later somehow).
Suppose that players A (ID: 123) and B (ID: 456) are dueling. Then, player B loses their internet connection.
Both players receive the callback:
-(void)match:(GKMatch *)match player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state
Player A, who is still connected to the match, gets that playerID is 456. This is indeed right because it was player B who disconnected.
Player B, on the other hand, gets that playerID is 123! Why? Shouldn't it be 456 since he himself is the one who disconnected first?
Anyway, this leads to my main problem: how am I suppose to tell who disconnected first from the match?
I need Player B to realize that he's the one who disconnected, so that the player receives a prompt saying "you lost the match because you disconnected".

Related

How to implement turn-based game in Orleans?

I am trying to implement a simple turn based game when N players can join the game before the games begins. Once the game begins, in each round each player takes a turn to roll a pair of dice in order they joined the game. There are three rounds in the game. At the end of the game, each player is awarded points.
For example, assume players P1, P2, P3, P4 joined the game in that order, then in the same order they will go about rolling the pair of dice. After three rounds, assume the results are
P1: (3,5), (4,1), (2,3)
P2: (5,5), (4,3), (1,3)
P3: (3,3), (4,4), (2,1)
For game, I am thinking something like:
public interface IGameBase : IGrainWithGuidKey
{
Task<bool> IsActive();
Task Join(IPlayerBase player);
Task Leave(IPlayerBase player);
}
public interface IGame<TState, in TRequest> : IGameBase
{
Task<TState> State();
Task<TState> Play(IPlayerBase player, TRequest request);
}
public interface IPlayerBase : IGrainWithGuidCompoundKey
{
Task<IPlayerInfo> Info();
Task<IEnumerable<Guid>> Games();
Task Subscribe(IGameBase game);
Task Unsubscribe(IGameBase game);
}
How should I implement a way to let players know that it is their turn to play?
I was able to resolve this by implementing it using streams. Basically sent a play message to the stream with game id and player id, and the players listen to these messages. The one whose turn it is will send a play move to game grain.
The code is here https://github.com/sheeri185adidam/orleanedgames

Esper: detect event start only

I'm new to Esper (NEsper, actually) and I've been trying (unsuccesfully) to create an statement to detect when an event starts.
For example, suppose I have an event type called "Started betting" and I want to consider it is happening after 10 minutes of having "proof" of that. With what I've been using as the statement, after 10 minutes the update() method is triggered every time there is "proof".
I've tried something like
from (...), StartedBetting as st
where st is null AND (...)
but didn't work (event was never detected).
Hope I've made myself clear.
Any hints will be appreciated.
So if I understand you right, when receiving any start-betting event you simply want to delay for 10 minutes and then get called? The "proof" part is not clear. But this would do it:
select * from pattern [every StartBetting -> timer:interval(10 min)]

Turn-based matchmaking not working in iOS 10

My game was working fine before iOS 10. Now, everyone with iOS 10 can't invite and play with who they want.
When a user says they want to play multiplayer, I create a GKMatchRequest like this:
GKMatchRequest *request = [[GKMatchRequest alloc] init];
request.minPlayers = 2;
request.maxPlayers = 4;
request.defaultNumberOfPlayers = 2;
I use a GKTurnBasedMatchmakerViewController to handle the invitation, etc. The user sees an interface that would let them change the number of players in the match and invite players. Let's say that they only want 2 players, so they leave that and they want to play with their friend. So, they use the interface to send an invite to their friend. The result, is that didFindMatch is called on my delegate with a GKMatch that has 4 participants. It should only have 2! The first participant is the local player and the other 3 have the status "Matching". So, their friend isn't even in the list. Does anyone have any suggestions for fixing this? This same code works fine in versions of iOS before iOS 10.
It appears that with IOS10, the defaultNumberOfPlayers isn't being honored.
Keep in mind that GKTurnBasedMatch has three types of players in a given match:
The Originator who creates the match
Players that were specifically invited into the match
Players that automatch into the match
If you look at the player status for all 4 players in the match (using your example), I suspect you will see the following results
Active (the originator)
Invited (the 1 person you invited)
Matching
Matching
If so, that would indicate that defaultNumberOfPlayers was ignored, and the match was created with a max of 4 players (1 originator, 1 invitee and 2 automatch slots).
The workaround seems to be to set the maxNumberOfPlayers to the desired cap, in this case, 2, when you create the match.

When using GKLocalPlayerListener's receivedTurnEventFor match, is there a way to determine if the turn event is the result of a matchmaking find?

In iOS 9 Apple deprecated the public func turnBasedMatchmakerViewController(_ viewController: GKTurnBasedMatchmakerViewController, didFind match: GKTurnBasedMatch)
method of the GKTurnBasedMatchmakerViewControllerDelegate.
Apple's direction is to use the func player(_ player: GKPlayer, receivedTurnEventFor match: GKTurnBasedMatch, didBecomeActive: Bool) method of GKLocalPlayerListener.
Using only receivedTurnEventFor match leads to the following scenario:
User taps + which displays a GKTurnBasedMatchmakerViewController.
User taps Play Now and Game Center will search for a match.
Game Center will return a match with empty matchData (a new match), or place the user into a match in progress — and the game is informed of this through receivedTurnEventFor match.
While it is simple enough to determine if a new match has been created (using matchData), there doesn't appear to be a way to determine if a match has been found vs a match being updated as all events flow through receivedTurnEventFor match.
My question is this:
When using GKLocalPlayerListener's receivedTurnEventFor match method, is there a way to determine if the turn event is the result of a matchmaking find?
There are four cases:
1. You join a new match and it's your turn (you are creator of the match)
2. You join a new match and it's other player turn (you found the match)
3. You join an exising match and it's your turn
4. You join an existing match and it's other player turn.
Cases 3 and 4 could be when you switch between matches or rejoin a match.
You can check match.currentPatriticant and lastTurnDate properties to determine which case takes place.
If a player just create a new match (receivedTurnEventFor is called as a result of matchmaking) the match.participants[0].lastTurnDate is nil and match.currentParticipant?.player?.playerID is equal GKLocalPlayer.localPlayer().playerID (case 1).
If you join an existing match:
match.participants[0].lastTurnDate is nil and match.currentParticipant?.player?.playerID is NOT equal GKLocalPlayer.localPlayer().playerID (you joined a new match, but other player is in a turn - case 2)
Do determine unambiguously cases 3 and 4 you can check lastTurnDate of all participant and compare local player with current player.
I don't think game center will help you on this one. It looks like yet another ambiguous call to the overloaded receivedTurnEventForMatch function. But, I believe you can manage this yourself since you know the originating player always sees the match first, before any of the other participants.
Looks like there are four cases here:
The player creates a new match with at least 1 automatch participant: you can detect this because the participants will be in "matching" status when you receive the new match. In this case, as originator, you can set flags in the match data that indicates which players have invites and which are automatches.
The player creates a new match with invites: In this case, all of the participants should be set in the newly received match. Again, you can set flags in the match data that subsequent players can read.
The player joins a match that was created as an automatch: If you support only 2 players, at the time player2 receives this match both playerIDs will be set. If you support more, there's an (unreliable) chance that some slots will still be set to automatch, but that's not very helpful. If the originating player set flags in the match data indicating which players were invited vs automatch slots, though, this player can determine their own status.
The player joins a match had one or more invitations: Same as the prior situation. The joining player can't determine anything useful from game center, and will rely on data added to the match by the originating player.

Esper: find earliest event within time after a trigger event arrives

My event stream generally contains an open event followed by a close event. Let's call them O and C, correspndingly. However, there are two particulars:
O may be followed by one or more O before C arrives
C may be missing completely (see below)
It is assumed that C should arrive not later than within time T after some O. Otherwise, C is considered missing. When a C eventually arrives, all open Os arrived earlier than T from this C are considered orphans and are of no interest.
I want esper to fire each pair of O followed by C, where earliest O not farther then T from C is selected. Any Os in between as well as before selected O are skipped.
For example,
O1 O2 O3 ... C
should select (O1,C) if datediff(O1, C) < T
should select (O2,C) if above is false and datediff(O2, C) < T
etc.
A lost my temper in approaching this problem. Looks like my mind is not compatible with esper. Your help is very appritiated.
This could be something like below, the idea is that when Event arrives we want to look at the last 1 minute of events and find the first one that matches the time diff. Have a second statement filter out those without matches, if that is desired.
insert into Pair
select , (select window().firstOf(v => v.time - e2.time < T) from Event.win:time(1 min) as e1) as matched
from Event as e2
select * from Pair where matched is not null

Resources