void PopulateUser(uint authGeneration, PlayerManager.FetchSelfResponse response) { Logger.d("Populating User"); if (authGeneration != mAuthGeneration) { Logger.d("Received user callback after signout occurred, ignoring"); return; } lock (AuthStateLock) { if (response.Status() != Status.ResponseStatus.VALID && response.Status() != Status.ResponseStatus.VALID_BUT_STALE) { Logger.e("Error retrieving user, signing out"); var localCallbacks = mPendingAuthCallbacks; mPendingAuthCallbacks = null; if (localCallbacks != null) { InvokeCallbackOnGameThread(localCallbacks, false); } SignOut(); return; } mUser = response.Self().AsPlayer(); mFriends = null; } Logger.d("Found User: "******"Maybe finish for User"); MaybeFinishAuthentication(); }
// called from game thread public void CreateWithInvitationScreen(int minOpponents, int maxOpponents, int variant, RealTimeMultiplayerListener listener) { Logger.d(string.Format("AndroidRtmpClient.CreateWithInvitationScreen, " + "opponents={0}-{1}, variant={2}", minOpponents, maxOpponents, variant)); if (!PrepareToCreateRoom("CreateWithInvitationScreen", listener)) { return; } mRtmpListener = listener; mVariant = variant; mClient.CallClientApi("rtmp create with invitation screen", () => { AndroidJavaClass klass = JavaUtil.GetClass( JavaConsts.SupportSelectOpponentsHelperActivity); mLaunchedExternalActivity = true; klass.CallStatic("launch", true, mClient.GetActivity(), new SelectOpponentsProxy(this), Logger.DebugLogEnabled, minOpponents, maxOpponents); }, (bool success) => { if (!success) { FailRoomSetup("Failed to create game because GoogleApiClient was disconnected"); } }); }
private void OnRealTimeMessageReceived(AndroidJavaObject message) { Logger.d("AndroidClient.OnRealTimeMessageReceived."); if (!CheckRtmpActive("OnRealTimeMessageReceived")) { return; } RealTimeMultiplayerListener listener = mRtmpListener; if (listener != null) { byte[] messageData; using (AndroidJavaObject messageBytes = message.Call <AndroidJavaObject>("getMessageData")) { messageData = JavaUtil.ConvertByteArray(messageBytes); } bool isReliable = message.Call <bool>("isReliable"); string senderId = message.Call <string>("getSenderParticipantId"); Utils.PlayGamesHelperObject.RunOnGameThread(() => { listener.OnRealTimeMessageReceived(isReliable, senderId, messageData); }); } message.Dispose(); }
// called from game thread public Participant GetParticipant(string id) { Logger.d("AndroidRtmpClient.GetParticipant: " + id); if (!CheckConnectedRoom("GetParticipant")) { return(null); } List <Participant> allParticipants; lock (mParticipantListsLock) { allParticipants = mAllParticipants; } if (allParticipants == null) { Logger.e("RtmpGetParticipant called without a valid room!"); return(null); } foreach (Participant p in allParticipants) { if (p.ParticipantId.Equals(id)) { return(p); } } Logger.e("Participant not found in room! id: " + id); return(null); }
/// <summary> /// Activates the Play Games platform as the implementation of Social.Active. /// After calling this method, you can call methods on Social.Active. For /// example, <c>Social.Active.Authenticate()</c>. /// </summary> /// <returns>The singleton <see cref="PlayGamesPlatform" /> instance.</returns> public static PlayGamesPlatform Activate() { Logger.d("Activating PlayGamesPlatform."); Social.Active = PlayGamesPlatform.Instance; Logger.d("PlayGamesPlatform activated: " + Social.Active); return PlayGamesPlatform.Instance; }
private void OnRoomConnected(int statusCode, AndroidJavaObject room) { Logger.d("AndroidClient.OnRoomConnected, status " + statusCode); if (!CheckRtmpActive("OnRoomConnected")) { return; } mRoom = room; UpdateRoom(); if (statusCode != 0) { FailRoomSetup("OnRoomConnected error code " + statusCode); } else { Logger.d("AndroidClient.OnRoomConnected: room setup succeeded!"); RealTimeMultiplayerListener listener = mRtmpListener; if (listener != null) { Logger.d("Invoking callback OnRoomConnected(true) to report success."); Utils.PlayGamesHelperObject.RunOnGameThread(() => { mDeliveredRoomConnected = true; listener.OnRoomConnected(true); }); } } }
public void ChooseMetadata(ISavedGameMetadata chosenMetadata) { var convertedMetadata = chosenMetadata as AndroidSnapshotMetadata; if (convertedMetadata != mOriginal && convertedMetadata != mUnmerged) { Logger.e("Caller attempted to choose a version of the metadata that was not part " + "of the conflict"); mCompleteCallback(SavedGameRequestStatus.BadInputError, null); return; } using (var task = mSnapshotsClient.Call <AndroidJavaObject>( "resolveConflict", mConflict.Call <string>("getConflictId"), convertedMetadata.JavaSnapshot)) { AndroidTaskUtils.AddOnSuccessListener <AndroidJavaObject>( task, dataOrConflict => mRetryFileOpen()); mAndroidSavedGameClient.AddOnFailureListenerWithSignOut( task, exception => { Logger.d("ChooseMetadata failed: " + exception.Call <string>("toString")); var status = mAndroidSavedGameClient.mAndroidClient.IsAuthenticated() ? SavedGameRequestStatus.InternalError : SavedGameRequestStatus.AuthenticationError; mCompleteCallback(status, null); } ); } }
void MaybeFinishAuthentication() { Action <bool> localCallbacks = null; lock (AuthStateLock) { // Only proceed if both the fetch-self and fetch-achievements callback have // completed. if (mUser == null || mAchievements == null) { Logger.d("Auth not finished. User="******" achievements=" + mAchievements); return; } Logger.d("Auth finished. Proceeding."); // Null out the pending callbacks - we will be invoking any pending ones. localCallbacks = mPendingAuthCallbacks; mPendingAuthCallbacks = null; mAuthState = AuthState.Authenticated; } if (localCallbacks != null) { Logger.d("Invoking Callbacks: " + localCallbacks); InvokeCallbackOnGameThread(localCallbacks, true); } }
private void DeliverRoomSetupProgressUpdate() { Logger.d("AndroidRtmpClient: DeliverRoomSetupProgressUpdate"); if (!mRtmpActive || mRoom == null || mDeliveredRoomConnected) { // no need to deliver progress return; } float progress = CalcRoomSetupPercentage(); if (progress < mLastReportedProgress) { progress = mLastReportedProgress; } else { mLastReportedProgress = progress; } Logger.d("room setup progress: " + progress + "%"); if (mRtmpListener != null) { Logger.d("Delivering progress to callback."); Utils.PlayGamesHelperObject.RunOnGameThread(() => { mRtmpListener.OnRoomSetupProgress(progress); }); } }
// called from game thread public void CreateQuickGame(int minOpponents, int maxOpponents, int variant, RealTimeMultiplayerListener listener) { Logger.d(string.Format("AndroidRtmpClient.CreateQuickGame, opponents={0}-{1}, " + "variant={2}", minOpponents, maxOpponents, variant)); if (!PrepareToCreateRoom("CreateQuickGame", listener)) { return; } mRtmpListener = listener; mVariant = variant; mClient.CallClientApi("rtmp create quick game", () => { AndroidJavaClass rtmpUtil = JavaUtil.GetClass(JavaConsts.SupportRtmpUtilsClass); rtmpUtil.CallStatic("createQuickGame", mClient.GHManager.GetApiClient(), minOpponents, maxOpponents, variant, new RoomUpdateProxy(this), new RoomStatusUpdateProxy(this), new RealTimeMessageReceivedProxy(this)); }, (bool success) => { if (!success) { FailRoomSetup("Failed to create game because GoogleApiClient was disconnected"); } }); }
private bool CheckRtmpActive(string method) { if (!mRtmpActive) { Logger.d("Got call to " + method + " with RTMP inactive. Ignoring."); return(false); } return(true); }
ConflictCallback ToOnGameThread(ConflictCallback conflictCallback) { return((resolver, original, originalData, unmerged, unmergedData) => { Logger.d("Invoking conflict callback"); PlayGamesHelperObject.RunOnGameThread(() => conflictCallback(resolver, original, originalData, unmerged, unmergedData)); }); }
// called from UI thread private void Clear(string reason) { Logger.d("RtmpClear: clearing RTMP (reason: " + reason + ")."); // leave the room, if we have one if (mRoom != null) { Logger.d("RtmpClear: Room still active, so leaving room."); string roomId = mRoom.Call <string>("getRoomId"); Logger.d("RtmpClear: room id to leave is " + roomId); // TODO: we are not specifying the callback from this API call to get // notified of when we *actually* leave the room. Perhaps we should do that, // in order to prevent the case where the developer tries to create a room // too soon after leaving the previous room, resulting in errors. mClient.GHManager.CallGmsApi("games.Games", "RealTimeMultiplayer", "leave", new NoopProxy(JavaConsts.RoomUpdateListenerClass), roomId); Logger.d("RtmpClear: left room."); mRoom = null; } else { Logger.d("RtmpClear: no room active."); } // call the OnLeftRoom() callback if needed if (mDeliveredRoomConnected) { Logger.d("RtmpClear: looks like we must call the OnLeftRoom() callback."); RealTimeMultiplayerListener listener = mRtmpListener; if (listener != null) { Logger.d("Calling OnLeftRoom() callback."); Utils.PlayGamesHelperObject.RunOnGameThread(() => { listener.OnLeftRoom(); }); } } else { Logger.d("RtmpClear: no need to call OnLeftRoom() callback."); } mLeaveRoomRequested = false; mDeliveredRoomConnected = false; mRoom = null; mConnectedParticipants = null; mAllParticipants = null; mSelf = null; mRtmpListener = null; mVariant = 0; mRtmpActive = false; mAccumulatedProgress = 0.0f; mLastReportedProgress = 0.0f; mLaunchedExternalActivity = false; Logger.d("RtmpClear: RTMP cleared."); }
// called from game thread public void SendMessage(bool reliable, string participantId, byte[] data, int offset, int length) { Logger.d(string.Format("AndroidRtmpClient.SendMessage, reliable={0}, " + "participantId={1}, data[]={2} bytes, offset={3}, length={4}", reliable, participantId, data.Length, offset, length)); if (!CheckConnectedRoom("SendMessage")) { return; } if (mSelf != null && mSelf.ParticipantId.Equals(participantId)) { Logger.d("Ignoring request to send message to self, " + participantId); return; } // Since we don't yet have API support for buffer/offset/length, convert // to a regular byte[] buffer: byte[] dataToSend = Misc.GetSubsetBytes(data, offset, length); if (participantId == null) { // this means "send to all" List <Participant> participants = GetConnectedParticipants(); foreach (Participant p in participants) { if (p.ParticipantId != null && !p.Equals(mSelf)) { SendMessage(reliable, p.ParticipantId, dataToSend, 0, dataToSend.Length); } } return; } mClient.CallClientApi("send message to " + participantId, () => { if (mRoom != null) { string roomId = mRoom.Call <string>("getRoomId"); if (reliable) { mClient.GHManager.CallGmsApi <int>("games.Games", "RealTimeMultiplayer", "sendReliableMessage", null, dataToSend, roomId, participantId); } else { mClient.GHManager.CallGmsApi <int>("games.Games", "RealTimeMultiplayer", "sendUnreliableMessage", dataToSend, roomId, participantId); } } else { Logger.w("Not sending message because real-time room was torn down."); } }, null); }
private void OnLeftRoom(int statusCode, AndroidJavaObject room) { Logger.d("AndroidClient.OnLeftRoom, status " + statusCode); if (!CheckRtmpActive("OnLeftRoom")) { return; } Clear("Got OnLeftRoom " + statusCode); }
internal IUserProfile[] GetFriends() { if (!IsAuthenticated()) { Logger.d("Cannot get friends when not authenticated!"); return new IUserProfile[0]; } return mClient.GetFriends(); }
/// <summary> /// Sets the default leaderboard for the leaderboard UI. After calling this /// method, a call to <see cref="ShowLeaderboardUI" /> will show only the specified /// leaderboard instead of showing the list of all leaderboards. /// </summary> /// <param name='lbid'> /// The ID of the leaderboard to display on the default UI. This may be a raw /// Google Play Games leaderboard ID or an alias configured through a call to /// <see cref="AddIdMapping" />. /// </param> public void SetDefaultLeaderboardForUI(string lbid) { Logger.d("SetDefaultLeaderboardForUI: " + lbid); if (lbid != null) { lbid = MapId(lbid); } mDefaultLbUi = lbid; }
/// <summary> /// Shows the standard Google Play Games achievements user interface, /// which allows the player to browse their achievements. /// </summary> /// <param name="callback">If non-null, the callback is invoked when /// the achievement UI is dismissed</param> public void ShowAchievementsUI(Action<UIStatus> callback) { if (!IsAuthenticated()) { Logger.e("ShowAchievementsUI can only be called after authentication."); return; } Logger.d("ShowAchievementsUI callback is " + callback); mClient.ShowAchievementsUI(callback); }
private void OnDisconnectedFromRoom(AndroidJavaObject room) { Logger.d("AndroidClient.OnDisconnectedFromRoom"); if (!CheckRtmpActive("OnDisconnectedFromRoom")) { return; } Clear("Got OnDisconnectedFromRoom"); }
private void OnP2PDisconnected(string participantId) { Logger.d("AndroidClient.OnP2PDisconnected: " + participantId); if (!CheckRtmpActive("OnP2PDisconnected")) { return; } UpdateRoom(); }
/// <summary> /// Authenticate the local user with the Google Play Games service. /// </summary> /// <param name='callback'> /// The callback to call when authentication finishes. It will be called /// with <c>true</c> if authentication was successful, <c>false</c> /// otherwise. /// </param> /// <param name='silent'> /// Indicates whether authentication should be silent. If <c>false</c>, /// authentication may show popups and interact with the user to obtain /// authorization. If <c>true</c>, there will be no popups or interaction with /// the user, and the authentication will fail instead if such interaction /// is required. A typical pattern is to try silent authentication on startup /// and, if that fails, present the user with a "Sign in" button that then /// triggers normal (not silent) authentication. /// </param> public void Authenticate(Action<bool> callback, bool silent) { // make a platform-specific Play Games client if (mClient == null) { Logger.d("Creating platform-specific Play Games client."); mClient = PlayGamesClientFactory.GetPlatformPlayGamesClient(mConfiguration); } // authenticate! mClient.Authenticate(callback, silent); }
private void OnPeersDisconnected(AndroidJavaObject room, AndroidJavaObject participantIds) { Logger.d("AndroidClient.OnPeersDisconnected."); if (!CheckRtmpActive("OnPeersDisconnected")) { return; } mRoom = room; UpdateRoom(); }
private void OnRoomConnecting(AndroidJavaObject room) { Logger.d("AndroidClient.OnRoomConnecting."); if (!CheckRtmpActive("OnRoomConnecting")) { return; } mRoom = room; UpdateRoom(); }
private static void InvokeCallbackOnGameThread <T>(Action <T> callback, T data) { if (callback == null) { return; } PlayGamesHelperObject.RunOnGameThread(() => { Logger.d("Invoking user callback on game thread"); callback(data); }); }
private void OnConnectedToRoom(AndroidJavaObject room) { Logger.d("AndroidClient.OnConnectedToRoom"); if (!CheckRtmpActive("OnConnectedToRoom")) { return; } mAccumulatedProgress += 10.0f; mRoom = room; UpdateRoom(); }
/// <summary> /// Shows the leaderboard UI and calls the specified callback upon /// completion. /// </summary> /// <param name="lbId">leaderboard ID, can be null meaning all leaderboards.</param> /// <param name="span">Timespan to display scores in the leaderboard.</param> /// <param name="callback">Callback to call. If null, nothing is called.</param> public void ShowLeaderboardUI(string lbId, LeaderboardTimeSpan span, Action<UIStatus> callback) { if (!IsAuthenticated()) { Logger.e("ShowLeaderboardUI can only be called after authentication."); callback(UIStatus.NotAuthorized); return; } Logger.d("ShowLeaderboardUI, lbId=" + lbId + " callback is " + callback); mClient.ShowLeaderboardUI(lbId, span, callback); }
// called from UI thread, on onStop public void OnStop() { // if we launched an external activity (like the "select opponents" UI) as part of // the process, we should NOT clear our RTMP state on OnStop because we will get // OnStop when that Activity launches. if (mLaunchedExternalActivity) { Logger.d("OnStop: EXTERNAL ACTIVITY is pending, so not clearing RTMP."); } else { Clear("leaving room because game is stopping."); } }
public IUserProfile[] GetFriends() { if (mFriends == null && !friendsLoading) { Logger.w("Getting friends before they are loaded!!!"); friendsLoading = true; LoadFriends((ok) => { Logger.d("loading: " + ok); friendsLoading = false; }); } return((mFriends == null) ? new IUserProfile[0] : mFriends.ToArray()); }
// called from the game thread public void DeclineInvitation(string invitationId) { Logger.d("AndroidRtmpClient.DeclineInvitation " + invitationId); mClient.ClearInvitationIfFromNotification(invitationId); mClient.CallClientApi("rtmp decline invitation", () => { mClient.GHManager.CallGmsApi("games.Games", "RealTimeMultiplayer", "declineInvitation", invitationId); }, (bool success) => { if (!success) { Logger.w("Failed to decline invitation. GoogleApiClient was disconnected"); } }); }
// called from UI thread private void FailRoomSetup(string reason) { Logger.d("Failing room setup: " + reason); RealTimeMultiplayerListener listener = mRtmpListener; Clear("Room setup failed: " + reason); if (listener != null) { Logger.d("Invoking callback OnRoomConnected(false) to signal failure."); Utils.PlayGamesHelperObject.RunOnGameThread(() => { listener.OnRoomConnected(false); }); } }