// // Copyright (C) 2014 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #if UNITY_ANDROID namespace GooglePlayGames { using System; using System.Collections.Generic; using GooglePlayGames.BasicApi; using GooglePlayGames.BasicApi.Events; using GooglePlayGames.BasicApi.Nearby; using GooglePlayGames.BasicApi.SavedGame; using GooglePlayGames.BasicApi.Video; using GooglePlayGames.OurUtils; using UnityEngine; using UnityEngine.SocialPlatforms; /// /// Provides access to the Google Play Games platform. This is an implementation of /// UnityEngine.SocialPlatforms.ISocialPlatform. Activate this platform by calling /// the method, then authenticate by calling /// the method. After authentication /// completes, you may call the other methods of this class. This is not a complete /// implementation of the ISocialPlatform interface. Methods lacking an implementation /// or whose behavior is at variance with the standard are noted as such. /// public class PlayGamesPlatform : ISocialPlatform { /// Singleton instance private static volatile PlayGamesPlatform sInstance = null; /// status of nearby connection initialization. private static volatile bool sNearbyInitializePending; /// Reference to the nearby client. /// This is static since it can be used without using play game services. private static volatile INearbyConnectionClient sNearbyConnectionClient; /// Configuration used to create this instance. private readonly PlayGamesClientConfiguration mConfiguration; /// The local user. private PlayGamesLocalUser mLocalUser = null; /// Reference to the platform specific implementation. private IPlayGamesClient mClient = null; /// the default leaderboard we show on ShowLeaderboardUI private string mDefaultLbUi = null; /// the mapping table from alias to leaderboard/achievement id. private Dictionary mIdMap = new Dictionary(); /// /// Initializes a new instance of the class. /// /// Implementation client to use for this instance. internal PlayGamesPlatform(IPlayGamesClient client) { this.mClient = Misc.CheckNotNull(client); this.mLocalUser = new PlayGamesLocalUser(this); this.mConfiguration = PlayGamesClientConfiguration.DefaultConfiguration; } /// /// Initializes a new instance of the class. /// /// Configuration object to use. private PlayGamesPlatform(PlayGamesClientConfiguration configuration) { GooglePlayGames.OurUtils.Logger.d("Creating new PlayGamesPlatform"); this.mLocalUser = new PlayGamesLocalUser(this); this.mConfiguration = configuration; } /// /// Gets or sets a value indicating whether debug logs are enabled. This property /// may be set before calling method. /// /// /// true if debug log enabled; otherwise, false. /// public static bool DebugLogEnabled { get { return GooglePlayGames.OurUtils.Logger.DebugLogEnabled; } set { GooglePlayGames.OurUtils.Logger.DebugLogEnabled = value; } } /// /// Gets the singleton instance of the Play Games platform. /// /// /// The instance. /// public static PlayGamesPlatform Instance { get { if (sInstance == null) { GooglePlayGames.OurUtils.Logger.d( "Instance was not initialized, using default configuration."); InitializeInstance(PlayGamesClientConfiguration.DefaultConfiguration); } return sInstance; } } /// /// Gets the nearby connection client. NOTE: Can be null until the nearby client /// is initialized. Call InitializeNearby to use callback to be notified when initialization /// is complete. /// /// The nearby. public static INearbyConnectionClient Nearby { get { if (sNearbyConnectionClient == null && !sNearbyInitializePending) { sNearbyInitializePending = true; InitializeNearby(null); } return sNearbyConnectionClient; } } /// Gets the saved game client object. /// The saved game client. public ISavedGameClient SavedGame { get { return mClient.GetSavedGameClient(); } } /// Gets the events client object. /// The events client. public IEventsClient Events { get { return mClient.GetEventsClient(); } } /// Gets the video client object. /// The video client. public IVideoClient Video { get { return mClient.GetVideoClient(); } } /// /// Gets the local user. /// /// /// The local user. /// public ILocalUser localUser { get { return mLocalUser; } } /// /// Initializes the instance of Play Game Services platform. /// /// This creates the singleton instance of the platform. /// Multiple calls to this method are ignored. /// /// Configuration to use when initializing. public static void InitializeInstance(PlayGamesClientConfiguration configuration) { if (sInstance == null || sInstance.mConfiguration != configuration) { sInstance = new PlayGamesPlatform(configuration); return; } GooglePlayGames.OurUtils.Logger.w( "PlayGamesPlatform already initialized. Ignoring this call."); } /// /// Initializes the nearby connection platform. /// /// This call initializes the nearby connection platform. This /// is independent of the Play Game Services initialization. Multiple /// calls to this method are ignored. /// /// Callback invoked when complete. public static void InitializeNearby(Action callback) { Debug.Log("Calling InitializeNearby!"); if (sNearbyConnectionClient == null) { #if UNITY_ANDROID && !UNITY_EDITOR NearbyConnectionClientFactory.Create(client => { Debug.Log("Nearby Client Created!!"); sNearbyConnectionClient = client; if (callback != null) { callback.Invoke(client); } else { Debug.Log("Initialize Nearby callback is null"); } }); #else sNearbyConnectionClient = new DummyNearbyConnectionClient(); if (callback != null) { callback.Invoke(sNearbyConnectionClient); } #endif } else if (callback != null) { Debug.Log("Nearby Already initialized: calling callback directly"); callback.Invoke(sNearbyConnectionClient); } else { Debug.Log("Nearby Already initialized"); } } /// /// Activates the Play Games platform as the implementation of Social.Active. /// After calling this method, you can call methods on Social.Active. For /// example, Social.Active.Authenticate(). /// /// The singleton instance. public static PlayGamesPlatform Activate() { GooglePlayGames.OurUtils.Logger.d("Activating PlayGamesPlatform."); Social.Active = PlayGamesPlatform.Instance; GooglePlayGames.OurUtils.Logger.d( "PlayGamesPlatform activated: " + Social.Active); return PlayGamesPlatform.Instance; } /// /// Sets the gravity for popups (Android only). /// /// This can only be called after authentication. It affects /// popups for achievements and other game services elements. /// Gravity for the popup. public void SetGravityForPopups(Gravity gravity) { mClient.SetGravityForPopups(gravity); } /// /// Specifies that the ID fromId should be implicitly replaced by toId /// on any calls that take a leaderboard or achievement ID. /// /// After a mapping is /// registered, you can use fromId instead of toId when making a call. /// For example, the following two snippets are equivalent: /// /// ReportProgress("Cfiwjew894_AQ", 100.0, callback); /// /// ...is equivalent to: /// /// AddIdMapping("super-combo", "Cfiwjew894_AQ"); /// ReportProgress("super-combo", 100.0, callback); /// /// /// /// The identifier to map. /// /// /// The identifier that fromId will be mapped to. /// public void AddIdMapping(string fromId, string toId) { mIdMap[fromId] = toId; } /// /// Authenticate the local user with the Google Play Games service. /// /// /// The callback to call when authentication finishes. It will be called /// with true if authentication was successful, false /// otherwise. /// public void Authenticate(Action callback) { Authenticate(callback, false); } /// /// Authenticate the local user with the Google Play Games service. /// /// /// The callback to call when authentication finishes. It will be called /// with true if authentication was successful, false /// otherwise. /// public void Authenticate(Action callback) { Authenticate(callback, false); } /// /// Authenticate the local user with the Google Play Games service. /// /// /// The callback to call when authentication finishes. It will be called /// with true if authentication was successful, false /// otherwise. /// /// /// Indicates whether authentication should be silent. If false, /// authentication may show popups and interact with the user to obtain /// authorization. If true, 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. /// public void Authenticate(Action callback, bool silent) { Authenticate((bool success, string msg) => callback(success), silent); } /// /// Authenticate the local user with the Google Play Games service. /// /// /// The callback to call when authentication finishes. It will be called /// with true if authentication was successful, false /// otherwise. /// /// /// Indicates whether authentication should be silent. If false, /// authentication may show popups and interact with the user to obtain /// authorization. If true, 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. /// public void Authenticate(Action callback, bool silent) { Authenticate(silent ? SignInInteractivity.NoPrompt : SignInInteractivity.CanPromptAlways, status => { if (status == SignInStatus.Success) { callback(true, "Authentication succeeded"); } else if (status == SignInStatus.Canceled) { callback(false, "Authentication canceled"); GooglePlayGames.OurUtils.Logger.d("Authentication canceled"); } else if (status == SignInStatus.DeveloperError) { callback(false, "Authentication failed - developer error"); GooglePlayGames.OurUtils.Logger.d("Authentication failed - developer error"); } else { callback(false, "Authentication failed"); GooglePlayGames.OurUtils.Logger.d("Authentication failed"); } }); } /// /// Authenticate the local user with the Google Play Games service. /// /// The callback to call when authentication finishes. /// public void Authenticate(SignInInteractivity signInInteractivity, Action callback) { // make a platform-specific Play Games client if (mClient == null) { GooglePlayGames.OurUtils.Logger.d( "Creating platform-specific Play Games client."); mClient = PlayGamesClientFactory.GetPlatformPlayGamesClient(mConfiguration); } if (callback == null) { callback = status => { }; } switch (signInInteractivity) { case SignInInteractivity.NoPrompt: mClient.Authenticate( /* silent= */ true, code => { // SignInStatus.UiSignInRequired is returned when silent sign in fails or when there is no // internet connection. if (code == SignInStatus.UiSignInRequired && Application.internetReachability == NetworkReachability.NotReachable) { callback(SignInStatus.NetworkError); } else { callback(code); } }); break; case SignInInteractivity.CanPromptAlways: mClient.Authenticate( /* silent= */ false, code => { // SignInStatus.Canceled is returned when interactive sign in fails or when there is no internet connection. if (code == SignInStatus.Canceled && Application.internetReachability == NetworkReachability.NotReachable) { callback(SignInStatus.NetworkError); } else { callback(code); } }); break; case SignInInteractivity.CanPromptOnce: // 1. Silent sign in first mClient.Authenticate( /* silent= */ true, silentSignInResultCode => { if (silentSignInResultCode == SignInStatus.Success) { OurUtils.Logger.d("Successful, triggering callback"); callback(silentSignInResultCode); return; } // 2. Check the shared pref and bail out if it's true. if (!SignInHelper.ShouldPromptUiSignIn()) { OurUtils.Logger.d( "User cancelled sign in attempt in the previous attempt. Triggering callback with silentSignInResultCode"); callback(silentSignInResultCode); return; } // 3. Check internet connection if (Application.internetReachability == NetworkReachability.NotReachable) { OurUtils.Logger.d("No internet connection"); callback(SignInStatus.NetworkError); return; } // 4. Interactive sign in mClient.Authenticate( /* silent= */ false, interactiveSignInResultCode => { // 5. Save that the user has cancelled the interactive sign in. if (interactiveSignInResultCode == SignInStatus.Canceled) { OurUtils.Logger.d("Cancelled, saving this to a shared pref"); SignInHelper.SetPromptUiSignIn(false); } callback(interactiveSignInResultCode); }); }); break; default: PlayGamesHelperObject.RunOnGameThread(() => callback(SignInStatus.Failed)); break; } } /// /// Provided for compatibility with ISocialPlatform. /// /// /// Unused parameter for this implementation. /// Callback invoked when complete. public void Authenticate(ILocalUser unused, Action callback) { Authenticate(callback, false); } /// /// Provided for compatibility with ISocialPlatform. /// /// /// Unused parameter for this implementation. /// Callback invoked when complete. public void Authenticate(ILocalUser unused, Action callback) { Authenticate(callback, false); } /// /// Determines whether the user is authenticated. /// /// /// true if the user is authenticated; otherwise, false. /// public bool IsAuthenticated() { return mClient != null && mClient.IsAuthenticated(); } /// Sign out. After signing out, /// Authenticate must be called again to sign back in. /// public void SignOut() { if (mClient != null) { mClient.SignOut(); } mLocalUser = new PlayGamesLocalUser(this); } /// /// Loads the users. /// /// User identifiers. /// Callback invoked when complete. public void LoadUsers(string[] userIds, Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "GetUserId() can only be called after authentication."); callback(new IUserProfile[0]); return; } mClient.LoadUsers(userIds, callback); } /// /// Returns the user's Google ID. /// /// /// The user's Google ID. No guarantees are made as to the meaning or format of /// this identifier except that it is unique to the user who is signed in. /// public string GetUserId() { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "GetUserId() can only be called after authentication."); return "0"; } return mClient.GetUserId(); } /// /// Get an id token for the user. /// public string GetIdToken() { if (mClient != null) { return mClient.GetIdToken(); } OurUtils.Logger.e("No client available, returning null."); return null; } /// /// Gets the server auth code. /// /// This code is used by the server application in order to get /// an oauth token. For how to use this acccess token please see: /// https://developers.google.com/drive/v2/web/auth/web-server. /// To get another server auth code after the initial one returned, call /// GetAnotherServerAuthCode(). /// public string GetServerAuthCode() { if (mClient != null && mClient.IsAuthenticated()) { return mClient.GetServerAuthCode(); } return null; } /// /// Gets another server auth code. /// /// This method should be called after authenticating, and exchanging /// the initial server auth code for a token. This is implemented by signing in /// silently, which if successful returns almost immediately and with a new /// server auth code. /// /// Calls Authenticate if needed when /// retrieving another auth code. /// Callback returning the auth code or null /// if there was an error. NOTE: This callback can return immediately. public void GetAnotherServerAuthCode(bool reAuthenticateIfNeeded, Action callback) { if (mClient != null && mClient.IsAuthenticated()) { mClient.GetAnotherServerAuthCode(reAuthenticateIfNeeded, callback); } else if (mClient != null && reAuthenticateIfNeeded) { mClient.Authenticate(false, (status) => { if (status == SignInStatus.Success) { callback(mClient.GetServerAuthCode()); } else { OurUtils.Logger.e("Re-authentication failed: " + status); callback(null); } }); } else { OurUtils.Logger.e("Cannot call GetAnotherServerAuthCode: not authenticated"); callback(null); } } /// /// Gets the user's email. /// public string GetUserEmail() { return mClient.GetUserEmail(); } /// /// Gets the player stats. /// /// Callback invoked when completed. public void GetPlayerStats(Action callback) { if (mClient != null && mClient.IsAuthenticated()) { mClient.GetPlayerStats(callback); } else { GooglePlayGames.OurUtils.Logger.e( "GetPlayerStats can only be called after authentication."); callback(CommonStatusCodes.SignInRequired, new PlayerStats()); } } /// /// Returns the user's display name. /// /// /// The user display name (e.g. "Bruno Oliveira") /// public string GetUserDisplayName() { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "GetUserDisplayName can only be called after authentication."); return string.Empty; } return mClient.GetUserDisplayName(); } /// /// Returns the user's avatar URL if they have one. /// /// /// The URL, or null if the user is not authenticated or does not have /// an avatar. /// public string GetUserImageUrl() { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "GetUserImageUrl can only be called after authentication."); return null; } return mClient.GetUserImageUrl(); } /// /// 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 instead. /// /// /// 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 . /// /// /// 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%). /// /// /// Callback that will be called to report the result of the operation: true on /// success, false otherwise. /// public void ReportProgress(string achievementID, double progress, Action callback) { callback = ToOnGameThread(callback); if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "ReportProgress can only be called after authentication."); callback.Invoke(false); return; } // map ID, if it's in the dictionary GooglePlayGames.OurUtils.Logger.d("ReportProgress, " + achievementID + ", " + progress); achievementID = MapId(achievementID); // if progress is 0.0, we just want to reveal it if (progress < 0.000001) { GooglePlayGames.OurUtils.Logger.d( "Progress 0.00 interpreted as request to reveal."); mClient.RevealAchievement(achievementID, callback); return; } mClient.LoadAchievements(ach => { for (int i = 0; i < ach.Length; i++) { if (ach[i].Id == achievementID) { if (ach[i].IsIncremental) { GooglePlayGames.OurUtils.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] GooglePlayGames.OurUtils.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."); } mClient.SetStepsAtLeast(achievementID, progressToSteps(progress, ach[i].TotalSteps), callback); } else { if (progress >= 100) { // unlock it! GooglePlayGames.OurUtils.Logger.d("Progress " + progress + " interpreted as UNLOCK."); mClient.UnlockAchievement(achievementID, callback); } else { // not enough to unlock GooglePlayGames.OurUtils.Logger.d( "Progress " + progress + " not enough to unlock non-incremental achievement."); callback.Invoke(false); } } return; } } // Achievement not found GooglePlayGames.OurUtils.Logger.e("Unable to locate achievement " + achievementID); callback.Invoke(false); }); } internal static int progressToSteps(double progress, int totalSteps) { return (progress >= 100.0) ? totalSteps : (int) (progress * totalSteps / 100.0); } /// /// Reveals the achievement with the passed identifier. This is a Play Games extension of the ISocialPlatform API. /// /// If the operation succeeds, the callback /// will be invoked on the game thread with true. If the operation fails, the /// callback will be invoked with false. This operation will immediately fail if /// the user is not authenticated (i.e. the callback will immediately be invoked with /// false). If the achievement is already in a revealed state, this call will /// succeed immediately. /// /// /// The ID of the achievement to increment. This can be a raw Google Play /// Games achievement ID (alphanumeric string), or an alias that was previously configured /// by a call to . /// /// /// The callback to call to report the success or failure of the operation. The callback /// will be called with true to indicate success or false for failure. /// public void RevealAchievement(string achievementID, Action callback = null) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "RevealAchievement can only be called after authentication."); if (callback != null) { callback.Invoke(false); } return; } // map ID, if it's in the dictionary GooglePlayGames.OurUtils.Logger.d( "RevealAchievement: " + achievementID); achievementID = MapId(achievementID); mClient.RevealAchievement(achievementID, callback); } /// /// Unlocks the achievement with the passed identifier. This is a Play Games extension of the ISocialPlatform API. /// /// If the operation succeeds, the callback /// will be invoked on the game thread with true. If the operation fails, the /// callback will be invoked with false. This operation will immediately fail if /// the user is not authenticated (i.e. the callback will immediately be invoked with /// false). If the achievement is already unlocked, this call will /// succeed immediately. /// /// /// The ID of the achievement to increment. This can be a raw Google Play /// Games achievement ID (alphanumeric string), or an alias that was previously configured /// by a call to . /// /// /// The callback to call to report the success or failure of the operation. The callback /// will be called with true to indicate success or false for failure. /// public void UnlockAchievement(string achievementID, Action callback = null) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "UnlockAchievement can only be called after authentication."); if (callback != null) { callback.Invoke(false); } return; } // map ID, if it's in the dictionary GooglePlayGames.OurUtils.Logger.d( "UnlockAchievement: " + achievementID); achievementID = MapId(achievementID); mClient.UnlockAchievement(achievementID, callback); } /// /// Increments an achievement. This is a Play Games extension of the ISocialPlatform API. /// /// /// The ID of the achievement to increment. This can be a raw Google Play /// Games achievement ID (alphanumeric string), or an alias that was previously configured /// by a call to . /// /// /// The number of steps to increment the achievement by. /// /// /// The callback to call to report the success or failure of the operation. The callback /// will be called with true to indicate success or false for failure. /// public void IncrementAchievement(string achievementID, int steps, Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "IncrementAchievement can only be called after authentication."); if (callback != null) { callback.Invoke(false); } return; } // map ID, if it's in the dictionary GooglePlayGames.OurUtils.Logger.d( "IncrementAchievement: " + achievementID + ", steps " + steps); achievementID = MapId(achievementID); mClient.IncrementAchievement(achievementID, steps, callback); } /// /// Set an achievement to have at least the given number of steps completed. /// Calling this method while the achievement already has more steps than /// the provided value is a no-op. Once the achievement reaches the /// maximum number of steps, the achievement is automatically unlocked, /// and any further mutation operations are ignored. /// /// /// The ID of the achievement to increment. This can be a raw Google Play /// Games achievement ID (alphanumeric string), or an alias that was previously configured /// by a call to . /// /// /// The number of steps to increment the achievement by. /// /// /// The callback to call to report the success or failure of the operation. The callback /// will be called with true to indicate success or false for failure. /// public void SetStepsAtLeast(string achievementID, int steps, Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "SetStepsAtLeast can only be called after authentication."); if (callback != null) { callback.Invoke(false); } return; } // map ID, if it's in the dictionary GooglePlayGames.OurUtils.Logger.d( "SetStepsAtLeast: " + achievementID + ", steps " + steps); achievementID = MapId(achievementID); mClient.SetStepsAtLeast(achievementID, steps, callback); } /// /// Loads the Achievement descriptions. /// /// The callback to receive the descriptions public void LoadAchievementDescriptions(Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "LoadAchievementDescriptions can only be called after authentication."); if (callback != null) { callback.Invoke(null); } return; } mClient.LoadAchievements(ach => { IAchievementDescription[] data = new IAchievementDescription[ach.Length]; for (int i = 0; i < data.Length; i++) { data[i] = new PlayGamesAchievement(ach[i]); } callback.Invoke(data); }); } /// /// Loads the achievement state for the current user. /// /// The callback to receive the achievements public void LoadAchievements(Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e("LoadAchievements can only be called after authentication."); callback.Invoke(null); return; } mClient.LoadAchievements(ach => { IAchievement[] data = new IAchievement[ach.Length]; for (int i = 0; i < data.Length; i++) { data[i] = new PlayGamesAchievement(ach[i]); } callback.Invoke(data); }); } /// /// Creates an achievement object which may be subsequently used to report an /// achievement. /// /// /// The achievement object. /// public IAchievement CreateAchievement() { return new PlayGamesAchievement(); } /// /// Reports a score to a leaderboard. /// /// /// The score to report. /// /// /// The ID of the leaderboard on which the score is to be posted. This may be a raw /// Google Play Games leaderboard ID or an alias configured through a call to /// . /// /// /// The callback to call to report the success or failure of the operation. The callback /// will be called with true to indicate success or false for failure. /// public void ReportScore(long score, string board, Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e("ReportScore can only be called after authentication."); if (callback != null) { callback.Invoke(false); } return; } GooglePlayGames.OurUtils.Logger.d("ReportScore: score=" + score + ", board=" + board); string leaderboardId = MapId(board); mClient.SubmitScore(leaderboardId, score, callback); } /// /// Submits the score for the currently signed-in player /// to the leaderboard associated with a specific id /// and metadata (such as something the player did to earn the score). /// /// Score to report. /// leaderboard id. /// metadata about the score. /// Callback invoked upon completion. public void ReportScore(long score, string board, string metadata, Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e("ReportScore can only be called after authentication."); if (callback != null) { callback.Invoke(false); } return; } GooglePlayGames.OurUtils.Logger.d("ReportScore: score=" + score + ", board=" + board + " metadata=" + metadata); string leaderboardId = MapId(board); mClient.SubmitScore(leaderboardId, score, metadata, callback); } /// /// Loads the scores relative the player. /// /// This returns the 25 /// (which is the max results returned by the SDK per call) scores /// that are around the player's score on the Public, all time leaderboard. /// Use the overloaded methods which are specific to GPGS to modify these /// parameters. /// /// Leaderboard Id /// Callback to invoke when completed. public void LoadScores(string leaderboardId, Action callback) { LoadScores( leaderboardId, LeaderboardStart.PlayerCentered, mClient.LeaderboardMaxResults(), LeaderboardCollection.Public, LeaderboardTimeSpan.AllTime, (scoreData) => callback(scoreData.Scores)); } /// /// Loads the scores using the provided parameters. This call may fail when trying to load friends with /// ResponseCode.ResolutionRequired if the user has not share the friends list with the game. In this case, use /// AskForLoadFriendsResolution to request access. /// /// Leaderboard identifier. /// Start either top scores, or player centered. /// Row count. the number of rows to return. /// Collection. social or public /// Time span. daily, weekly, all-time /// Callback to invoke when completed. public void LoadScores( string leaderboardId, LeaderboardStart start, int rowCount, LeaderboardCollection collection, LeaderboardTimeSpan timeSpan, Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e("LoadScores can only be called after authentication."); callback(new LeaderboardScoreData( leaderboardId, ResponseStatus.NotAuthorized)); return; } mClient.LoadScores( leaderboardId, start, rowCount, collection, timeSpan, callback); } /// /// Loads more scores. This call may fail when trying to load friends with /// ResponseCode.ResolutionRequired if the user has not share the friends list with the game. In this case, use /// AskForLoadFriendsResolution to request access. /// /// This is used to load the next "page" of scores. /// Token used to recording the loading. /// Row count. /// Callback invoked when complete. public void LoadMoreScores( ScorePageToken token, int rowCount, Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e("LoadMoreScores can only be called after authentication."); callback( new LeaderboardScoreData( token.LeaderboardId, ResponseStatus.NotAuthorized)); return; } mClient.LoadMoreScores(token, rowCount, callback); } /// /// Returns a leaderboard object that can be configured to /// load scores. /// /// The leaderboard object. public ILeaderboard CreateLeaderboard() { return new PlayGamesLeaderboard(mDefaultLbUi); } /// /// Shows the standard Google Play Games achievements user interface, /// which allows the player to browse their achievements. /// public void ShowAchievementsUI() { ShowAchievementsUI(null); } /// /// Shows the standard Google Play Games achievements user interface, /// which allows the player to browse their achievements. /// /// If non-null, the callback is invoked when /// the achievement UI is dismissed public void ShowAchievementsUI(Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e("ShowAchievementsUI can only be called after authentication."); return; } GooglePlayGames.OurUtils.Logger.d("ShowAchievementsUI callback is " + callback); mClient.ShowAchievementsUI(callback); } /// /// Shows the standard Google Play Games leaderboards user interface, /// which allows the player to browse their leaderboards. If you have /// configured a specific leaderboard as the default through a call to /// , the UI will show that /// specific leaderboard only. Otherwise, a list of all the leaderboards /// will be shown. /// public void ShowLeaderboardUI() { GooglePlayGames.OurUtils.Logger.d("ShowLeaderboardUI with default ID"); ShowLeaderboardUI(MapId(mDefaultLbUi), null); } /// /// Shows the standard Google Play Games leaderboard UI for the given /// leaderboard. /// /// /// The ID of the leaderboard to display. This may be a raw /// Google Play Games leaderboard ID or an alias configured through a call to /// . /// public void ShowLeaderboardUI(string leaderboardId) { if (leaderboardId != null) { leaderboardId = MapId(leaderboardId); } ShowLeaderboardUI(leaderboardId, LeaderboardTimeSpan.AllTime, null); } /// /// Shows the leaderboard UI and calls the specified callback upon /// completion. /// /// leaderboard ID, can be null meaning all leaderboards. /// Callback to call. If null, nothing is called. public void ShowLeaderboardUI(string leaderboardId, Action callback) { ShowLeaderboardUI(leaderboardId, LeaderboardTimeSpan.AllTime, callback); } /// /// Shows the leaderboard UI and calls the specified callback upon /// completion. /// /// leaderboard ID, can be null meaning all leaderboards. /// Timespan to display scores in the leaderboard. /// Callback to call. If null, nothing is called. public void ShowLeaderboardUI( string leaderboardId, LeaderboardTimeSpan span, Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e("ShowLeaderboardUI can only be called after authentication."); if (callback != null) { callback(UIStatus.NotAuthorized); } return; } GooglePlayGames.OurUtils.Logger.d("ShowLeaderboardUI, lbId=" + leaderboardId + " callback is " + callback); mClient.ShowLeaderboardUI(leaderboardId, span, callback); } /// /// Sets the default leaderboard for the leaderboard UI. After calling this /// method, a call to will show only the specified /// leaderboard instead of showing the list of all leaderboards. /// /// /// 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 /// . /// public void SetDefaultLeaderboardForUI(string lbid) { GooglePlayGames.OurUtils.Logger.d("SetDefaultLeaderboardForUI: " + lbid); if (lbid != null) { lbid = MapId(lbid); } mDefaultLbUi = lbid; } /// /// Loads the friends that also play this game. See loadConnectedPlayers. /// /// This is a callback variant of LoadFriends. When completed, /// the friends list set in the user object, so they can accessed via the /// friends property as needed. /// /// The current local user /// Callback invoked when complete. public void LoadFriends(ILocalUser user, Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "LoadScores can only be called after authentication."); if (callback != null) { callback(false); } return; } mClient.LoadFriends(callback); } /// /// Loads the leaderboard based on the constraints in the leaderboard /// object. /// /// The leaderboard object. This is created by /// calling CreateLeaderboard(), and then initialized appropriately. /// Callback invoked when complete. public void LoadScores(ILeaderboard board, Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e("LoadScores can only be called after authentication."); if (callback != null) { callback(false); } return; } LeaderboardTimeSpan timeSpan; switch (board.timeScope) { case TimeScope.AllTime: timeSpan = LeaderboardTimeSpan.AllTime; break; case TimeScope.Week: timeSpan = LeaderboardTimeSpan.Weekly; break; case TimeScope.Today: timeSpan = LeaderboardTimeSpan.Daily; break; default: timeSpan = LeaderboardTimeSpan.AllTime; break; } ((PlayGamesLeaderboard) board).loading = true; GooglePlayGames.OurUtils.Logger.d("LoadScores, board=" + board + " callback is " + callback); mClient.LoadScores( board.id, LeaderboardStart.PlayerCentered, board.range.count > 0 ? board.range.count : mClient.LeaderboardMaxResults(), board.userScope == UserScope.FriendsOnly ? LeaderboardCollection.Social : LeaderboardCollection.Public, timeSpan, (scoreData) => HandleLoadingScores( (PlayGamesLeaderboard) board, scoreData, callback)); } /// Asks user to give permissions for the given scopes. /// Scope to ask permission for /// Callback used to indicate the outcome of the operation. public void RequestPermission(string scope, Action callback) { RequestPermissions(new string[] {scope}, callback); } /// Asks user to give permissions for the given scopes. /// List of scopes to ask permission for /// Callback used to indicate the outcome of the operation. public void RequestPermissions(string[] scopes, Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "HasPermissions can only be called after authentication."); callback(SignInStatus.NotAuthenticated); return; } mClient.RequestPermissions(scopes, callback); } /// Returns whether or not user has given permissions for given scopes. /// scope /// true, if given, false otherwise. public bool HasPermission(string scope) { return HasPermissions(new string[] {scope}); } /// Returns whether or not user has given permissions for given scopes. /// array of scopes /// true, if given, false otherwise. public bool HasPermissions(string[] scopes) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "HasPermissions can only be called after authentication."); return false; } return mClient.HasPermissions(scopes); } /// /// Check if the leaderboard is currently loading. /// /// true, if loading was gotten, false otherwise. /// The leaderboard to check for loading in progress public bool GetLoading(ILeaderboard board) { return board != null && board.loading; } /// /// Shows the Player Profile UI for the given user identifier. /// /// User Identifier. /// /// The game's own display name of the player referred to by userId. /// /// /// The game's own display name of the current player. /// /// Callback invoked upon completion. public void ShowCompareProfileWithAlternativeNameHintsUI(string userId, string otherPlayerInGameName, string currentPlayerInGameName, Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "ShowCompareProfileWithAlternativeNameHintsUI can only be called after authentication."); InvokeCallbackOnGameThread(callback, UIStatus.NotAuthorized); return; } GooglePlayGames.OurUtils.Logger.d( "ShowCompareProfileWithAlternativeNameHintsUI, userId=" + userId + " callback is " + callback); mClient.ShowCompareProfileWithAlternativeNameHintsUI(userId, otherPlayerInGameName, currentPlayerInGameName, callback); } /// /// Returns if the user has allowed permission for the game to access the friends list. /// /// If true, this call will clear any locally cached data and /// attempt to fetch the latest data from the server. Normally, this should be set to {@code /// false} to gain advantages of data caching. /// Callback invoked upon completion. public void GetFriendsListVisibility(bool forceReload, Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "GetFriendsListVisibility can only be called after authentication."); InvokeCallbackOnGameThread(callback, FriendsListVisibilityStatus.NotAuthorized); return; } GooglePlayGames.OurUtils.Logger.d("GetFriendsListVisibility, callback is " + callback); mClient.GetFriendsListVisibility(forceReload, callback); } /// /// Shows the appropriate platform-specific friends sharing UI. /// The callback to invoke when complete. If null, /// no callback is called. /// public void AskForLoadFriendsResolution(Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "AskForLoadFriendsResolution can only be called after authentication."); InvokeCallbackOnGameThread(callback, UIStatus.NotAuthorized); return; } GooglePlayGames.OurUtils.Logger.d("AskForLoadFriendsResolution callback is " + callback); mClient.AskForLoadFriendsResolution(callback); } /// /// Gets status of the last call to load friends. /// public LoadFriendsStatus GetLastLoadFriendsStatus() { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "GetLastLoadFriendsStatus can only be called after authentication."); return LoadFriendsStatus.NotAuthorized; } return mClient.GetLastLoadFriendsStatus(); } /// /// Loads the first page of the user's friends /// /// /// The number of entries to request for this initial page. Note that if cached /// data already exists, the returned buffer may contain more than this size, but it is /// guaranteed to contain at least this many if the collection contains enough records. /// /// /// If true, this call will clear any locally cached data and attempt to /// fetch the latest data from the server. This would commonly be used for something like a /// user-initiated refresh. Normally, this should be set to {@code false} to gain advantages /// of data caching. Callback invoked upon /// completion. public void LoadFriends(int pageSize, bool forceReload, Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "LoadFriends can only be called after authentication."); InvokeCallbackOnGameThread(callback, LoadFriendsStatus.NotAuthorized); return; } mClient.LoadFriends(pageSize, forceReload, callback); } /// /// Loads the friends list page /// /// /// The number of entries to request for this initial page. Note that if cached /// data already exists, the returned buffer may contain more than this size, but it is /// guaranteed to contain at least this many if the collection contains enough records. /// /// public void LoadMoreFriends(int pageSize, Action callback) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.e( "LoadMoreFriends can only be called after authentication."); InvokeCallbackOnGameThread(callback, LoadFriendsStatus.NotAuthorized); return; } mClient.LoadMoreFriends(pageSize, callback); } /// /// Handles the processing of scores during loading. /// /// leaderboard being loaded /// Score data. /// Callback invoked when complete. internal void HandleLoadingScores( PlayGamesLeaderboard board, LeaderboardScoreData scoreData, Action callback) { bool ok = board.SetFromData(scoreData); if (ok && !board.HasAllScores() && scoreData.NextPageToken != null) { int rowCount = board.range.count - board.ScoreCount; // need to load more scores mClient.LoadMoreScores( scoreData.NextPageToken, rowCount, (nextScoreData) => HandleLoadingScores(board, nextScoreData, callback)); } else { callback(ok); } } /// /// Internal implmentation of getFriends.Gets the friends. /// /// The friends. internal IUserProfile[] GetFriends() { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.d("Cannot get friends when not authenticated!"); return new IUserProfile[0]; } return mClient.GetFriends(); } /// /// Maps the alias to the identifier. /// /// This maps an aliased ID to the actual id. The intent of /// this method is to allow easy to read constants to be used instead of /// the generated ids. /// /// The identifier, or null if not found. /// Alias to map private string MapId(string id) { if (id == null) { return null; } if (mIdMap.ContainsKey(id)) { string result = mIdMap[id]; GooglePlayGames.OurUtils.Logger.d("Mapping alias " + id + " to ID " + result); return result; } return id; } private static void InvokeCallbackOnGameThread(Action callback, T data) { if (callback == null) { return; } PlayGamesHelperObject.RunOnGameThread(() => { callback(data); }); } private static Action ToOnGameThread(Action toConvert) { if (toConvert == null) { return delegate { }; } return (val) => PlayGamesHelperObject.RunOnGameThread(() => toConvert(val)); } } } #endif