// 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); }
public static void InitializeInstance(PlayGamesClientConfiguration configuration) { if (sInstance != null) { Logger.w("PlayGamesPlatform already initialized. Ignoring this call."); return; } sInstance = new PlayGamesPlatform(configuration); }
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"); } }); }
/// <summary> /// Calls a method on a java object while handling null return values. /// Sadly, it appears that calling a method that returns a null Object in Java so we work /// around this by catching null pointer exceptions a checking for the word "null". /// </summary> internal static AndroidJavaObject NullSafeCall(this AndroidJavaObject target, string methodName, params object[] args) { try { return(target.Call <AndroidJavaObject>(methodName, args)); } catch (Exception ex) { if (ex.Message.Contains("null")) { // expected -- means method returned null return(null); } else { Logger.w("CallObjectMethod exception: " + ex); return(null); } } }
// called from game thread public void LeaveRoom() { Logger.d("AndroidRtmpClient.LeaveRoom"); // if we are setting up a room but haven't got the room yet, we can't // leave the room now, we have to defer it to a later time when we have the room if (mRtmpActive && mRoom == null) { Logger.w("AndroidRtmpClient.LeaveRoom: waiting for room; deferring leave request."); mLeaveRoomRequested = true; } else { mClient.CallClientApi("leave room", () => { Clear("LeaveRoom called"); }, null); } }
private static InitializationStatus ToStatus(S.InitializationStatus status) { switch (status) { case S.InitializationStatus.VALID: return(InitializationStatus.Success); case S.InitializationStatus.ERROR_INTERNAL: return(InitializationStatus.InternalError); case S.InitializationStatus.ERROR_VERSION_UPDATE_REQUIRED: return(InitializationStatus.VersionUpdateRequired); default: Logger.w("Unknown initialization status: " + status); return(InitializationStatus.InternalError); } }
private void OnInvitationInboxResult(bool success, string invitationId) { Logger.d("AndroidRtmpClient.OnInvitationInboxResult, " + "success=" + success + ", invitationId=" + invitationId); if (!CheckRtmpActive("OnInvitationInboxResult")) { return; } // we now do not have an external Activity that we launched mLaunchedExternalActivity = false; if (!success || invitationId == null || invitationId.Length == 0) { Logger.w("Failed to setup room because invitation inbox UI failed."); FailRoomSetup("Invitation inbox UI failed."); return; } mClient.ClearInvitationIfFromNotification(invitationId); // we use CallClientApi instead of calling the API directly because we need // to make sure that we call it when the GoogleApiClient is connected, which is // not necessarily true at this point (we just came back from an external // activity) mClient.CallClientApi("accept invite from inbox", () => { Logger.d("Accepting invite from inbox via support lib."); AndroidJavaClass rtmpUtil = JavaUtil.GetClass(JavaConsts.SupportRtmpUtilsClass); rtmpUtil.CallStatic("accept", mClient.GHManager.GetApiClient(), invitationId, new RoomUpdateProxy(this), new RoomStatusUpdateProxy(this), new RealTimeMessageReceivedProxy(this)); }, (bool ok) => { if (!ok) { FailRoomSetup("GoogleApiClient lost connection."); } }); }
private void OnSelectOpponentsResult(bool success, AndroidJavaObject opponents, bool hasAutoMatch, AndroidJavaObject autoMatchCriteria) { Logger.d("AndroidRtmpClient.OnSelectOpponentsResult, success=" + success); if (!CheckRtmpActive("OnSelectOpponentsResult")) { return; } // we now do not have an external Activity that we launched mLaunchedExternalActivity = false; if (!success) { Logger.w("Room setup failed because select-opponents UI failed."); FailRoomSetup("Select opponents UI failed."); return; } // at this point, we have to create the room -- but we have to make sure that // our GoogleApiClient is connected before we do that. It might NOT be connected // right now because we just came back from calling an external Activity. // So we use CallClientApi to make sure we are only doing this at the right time: mClient.CallClientApi("creating room w/ select-opponents result", () => { Logger.d("Creating room via support lib's RtmpUtil."); AndroidJavaClass rtmpUtil = JavaUtil.GetClass(JavaConsts.SupportRtmpUtilsClass); rtmpUtil.CallStatic("create", mClient.GHManager.GetApiClient(), opponents, mVariant, hasAutoMatch ? autoMatchCriteria : null, new RoomUpdateProxy(this), new RoomStatusUpdateProxy(this), new RealTimeMessageReceivedProxy(this)); }, (bool ok) => { if (!ok) { FailRoomSetup("GoogleApiClient lost connection"); } }); }
/// <summary> /// Reports the progress of an achievement (reveal, unlock or increment). This method attempts /// to implement the expected behavior of ISocialPlatform.ReportProgress as closely as possible, /// as described below. Although this method works with incremental achievements for compatibility /// purposes, calling this method for incremental achievements is not recommended, /// since the Play Games API exposes incremental achievements in a very different way /// than the interface presented by ISocialPlatform.ReportProgress. The implementation of this /// method for incremental achievements attempts to produce the correct result, but may be /// imprecise. If possible, call <see cref="IncrementAchievement" /> instead. /// </summary> /// <param name='achievementID'> /// The ID of the achievement to unlock, reveal or increment. This can be a raw Google Play /// Games achievement ID (alphanumeric string), or an alias that was previously configured /// by a call to <see cref="AddIdMapping" />. /// </param> /// <param name='progress'> /// Progress of the achievement. If the achievement is standard (not incremental), then /// a progress of 0.0 will reveal the achievement and 100.0 will unlock it. Behavior of other /// values is undefined. If the achievement is incremental, then this value is interpreted /// as the total percentage of the achievement's progress that the player should have /// as a result of this call (regardless of the progress they had before). So if the /// player's previous progress was 30% and this call specifies 50.0, the new progress will /// be 50% (not 80%). /// </param> /// <param name='callback'> /// Callback that will be called to report the result of the operation: <c>true</c> on /// success, <c>false</c> otherwise. /// </param> public void ReportProgress(string achievementID, double progress, Action<bool> callback) { if (!IsAuthenticated()) { Logger.e("ReportProgress can only be called after authentication."); if (callback != null) { callback.Invoke(false); } return; } // map ID, if it's in the dictionary Logger.d("ReportProgress, " + achievementID + ", " + progress); achievementID = MapId(achievementID); // if progress is 0.0, we just want to reveal it if (progress < 0.000001) { Logger.d("Progress 0.00 interpreted as request to reveal."); mClient.RevealAchievement(achievementID, callback); return; } // figure out if it's a standard or incremental achievement bool isIncremental = false; int curSteps = 0, totalSteps = 0; Achievement ach = mClient.GetAchievement(achievementID); if (ach == null) { Logger.w("Unable to locate achievement " + achievementID); Logger.w("As a quick fix, assuming it's standard."); isIncremental = false; } else { isIncremental = ach.IsIncremental; curSteps = ach.CurrentSteps; totalSteps = ach.TotalSteps; Logger.d("Achievement is " + (isIncremental ? "INCREMENTAL" : "STANDARD")); if (isIncremental) { Logger.d("Current steps: " + curSteps + "/" + totalSteps); } } // do the right thing depending on the achievement type if (isIncremental) { // increment it to the target percentage (approximate) Logger.d("Progress " + progress + " interpreted as incremental target (approximate)."); if (progress >= 0.0 && progress <= 1.0) { // in a previous version, incremental progress was reported by using the range [0-1] Logger.w("Progress " + progress + " is less than or equal to 1. You might be trying to use values in the range of [0,1], while values are expected to be within the range [0,100]. If you are using the latter, you can safely ignore this message."); } int targetSteps = (int)((progress / 100) * totalSteps); int numSteps = targetSteps - curSteps; Logger.d("Target steps: " + targetSteps + ", cur steps:" + curSteps); Logger.d("Steps to increment: " + numSteps); if (numSteps > 0) { mClient.IncrementAchievement(achievementID, numSteps, callback); } } else if (progress >= 100) { // unlock it! Logger.d("Progress " + progress + " interpreted as UNLOCK."); mClient.UnlockAchievement(achievementID, callback); } else { // not enough to unlock Logger.d("Progress " + progress + " not enough to unlock non-incremental achievement."); } }