/// <summary>
    /// Fetches the names of the available leaderboards, saving them in the drLeaderboards.available array.
    /// </summary>
    /// <param name="success">Callback triggers on successful data save.</param>
    /// <param name="error">Callback triggers on error.</param>
    public static Coroutine FetchAvailableLeaderboards(dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
    {
        drWWW www = new drWWW(drAPI.leaderboardList);

        www.OnSuccess += delegate {
            ArrayList result = www.result as ArrayList;

            if (result.Count == 0) {
                availableLeaderboards = new string[0];
                drDebug.LogWarning("No leaderboards have been set up");
           		return;
            }

            availableLeaderboards = new string[result.Count];

            for (int i = 0; i < result.Count; i++) {
                availableLeaderboards[i] = result[i].ToString();
            }

            drDebug.Log("Fetched leaderboards available");
        }; www.OnSuccess += success;

        www.OnError += delegate (string errorMessage) {
            drDebug.LogError("Error listing available leaderboards: " + errorMessage);
        }; www.OnError += error;

        return www.Fetch();
    }
 /// <summary>
 /// Saves global data, replacing all existing data.
 /// </summary>
 /// <param name="ns">The namespace of the data.</param>
 /// <param name="data">The data to save.</param>
 /// <param name="success">Callback triggers on successful data replacement.</param>
 /// <param name="error">Callback triggers on error.</param>
 public static Coroutine Replace(string ns, Hashtable data, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
 {
     return ReplaceData(ns, data, null, success, error);
 }
 /// <summary>
 /// Credits the wallet's value.
 /// </summary>
 /// <param name="name">The name of the wallet.</param>
 /// <param name="amount">The amount to increment.</param>
 /// <param name="success">Callback triggers on successful increment.</param>
 /// <param name="error">Callback triggers on error.</param>
 public static Coroutine Credit(string name, int amount, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
 {
     return Credit(name, amount, null, success, error);
 }
 /// <summary>
 /// Fetches the current Wallet value.
 /// </summary>
 /// <param name="name">The name of the wallet.</param>
 /// <param name="success">Callback triggers on successful fetch.</param>
 public static Coroutine FetchBalance(string name, dimeRocker.SuccessHandler success)
 {
     return FetchBalance(name, success, null);
 }
 /// <summary>
 /// Debits the wallet's value.
 /// </summary>
 /// <param name="name">The name of the wallet.</param>
 /// <param name="amount">The amount to Debit.</param>
 /// <param name="success">Callback triggers on successful debit.</param>
 public static Coroutine Debit(string name, int amount, dimeRocker.SuccessHandler success)
 {
     return Debit(name, amount, null, success, null);
 }
    /// <summary>
    /// Increments the Counter value(s) for one or more users.
    /// </summary>
    /// <param name="name">The name of the counter.</param>
    /// <param name="amount">The amount to increment.</param>
    /// <param name="userIds">The user's IDs.</param>
    /// <param name="success">Callback triggers on successful increment.</param>
    /// <param name="error">Callback triggers on error.</param>
    public static Coroutine Increment(string name, int amount, int[] userIds, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
    {
        drWWW www = GetWWW(drAPI.counterIncrement, name, userIds);
        www.AddField("amount", amount);

        www.OnSuccess += delegate {
            Hashtable result = www.result as Hashtable;

            if (result.Count == 0) {
                drDebug.LogWarning("No counters exist with the specified name and users");
           		return;
            }

            StoreReturn(name, result, userIds);
            drDebug.Log("Incremented counters");
        }; www.OnSuccess += success;

        www.OnError += delegate (string errorMessage) {
            drDebug.LogError("Error incrementing counters: " + errorMessage);
        }; www.OnError += error;

        return www.Fetch();
    }
 /// <summary>
 /// Fetches the current Counter value.
 /// </summary>
 /// <param name="name">The name of the counter.</param>
 /// <param name="success">Callback triggers on successful fetch.</param>
 /// <param name="error">Callback triggers on error.</param>
 public static Coroutine FetchCurrent(string name, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
 {
     return FetchCurrent(name, null, success, error);
 }
 /// <summary>
 /// Fetches the names of the available leaderboards, saving them in the drLeaderboards.available array.
 /// </summary>
 /// <param name="success">Callback triggers on successful data save.</param>
 public static Coroutine FetchAvailableLeaderboards(dimeRocker.SuccessHandler success)
 {
     return FetchAvailableLeaderboards(success, null);
 }
    /// <summary>
    /// Loads data.
    /// </summary>
    /// <param name="ns">The namespace of the data.</param>
    /// <param name="keys">Keys of the data to load.</param>
    /// <param name="userId">The user's ID.</param>
    /// <param name="success">Callback triggers on successful data save.</param>
    /// <param name="error">Callback triggers on error.</param>
    static Coroutine FetchData(string ns, string[] keys, int? userId, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
    {
        drWWW www = GetWWW(drAPI.dataGet, ns, userId);

        if (keys != null && keys.Length > 0) {
            string keysJson = JSON.JsonEncode(keys);
            www.AddField("keys", keysJson);
        }

        www.OnSuccess += delegate {
            Hashtable result = www.result as Hashtable;

            if (result.Count == 0) {
                drDebug.LogWarning("No data exists in the data store to load");
           		return;
            }

            StoreData(ns, result, userId);
            drDebug.Log("Loaded data");
        }; www.OnSuccess += success;

        www.OnError += delegate (string errorMessage) {
            drDebug.LogError("Error loading data: " + errorMessage);
        }; www.OnError += error;

        return www.Fetch();
    }
 /// <summary>
 /// Unsets data.
 /// </summary>
 /// <param name="ns">The namespace of the data.</param>
 /// <param name="keys">Keys of the data to unset.</param>
 /// <param name="userId">The user's ID.</param>
 /// <param name="success">Callback triggers on successful data save.</param>
 /// <param name="error">Callback triggers on error.</param>
 public static Coroutine Unset(string ns, string[] keys, int userId, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
 {
     return UnsetData(ns, keys, userId, success, error);
 }
 /// <summary>
 /// Unsets global data.
 /// </summary>
 /// <param name="ns">The namespace of the data.</param>
 /// <param name="keys">Keys of the data to unset</param>
 /// <param name="success">Callback triggers on successful data save.</param>
 public static Coroutine Unset(string ns, string[] keys, dimeRocker.SuccessHandler success)
 {
     return UnsetData(ns, keys, null, success, null);
 }
 /// <summary>
 /// Saves data.
 /// </summary>
 /// <param name="ns">The namespace of the data.</param>
 /// <param name="data">The data to save.</param>
 /// <param name="userId">The user's ID.</param>
 /// <param name="success">Callback triggers on successful data save.</param>
 /// <param name="error">Callback triggers on error.</param>
 public static Coroutine Set(string ns, Hashtable data, int userId, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
 {
     return SetData(ns, data, userId, success, error);
 }
 /// <summary>
 /// Saves namespaced global data.
 /// </summary>
 /// <param name="ns">The namespace of the data.</param>
 /// <param name="data">The data to save.</param>
 /// <param name="success">Callback triggers on successful data save.</param>
 public static Coroutine Set(string ns, Hashtable data, dimeRocker.SuccessHandler success)
 {
     return SetData(ns, data, null, success, null);
 }
 /// <summary>
 /// Saves user-specific data, replacing all existing user data.
 /// </summary>
 /// <param name="ns">The namespace of the data.</param>
 /// <param name="data">The data to save.</param>
 /// <param name="userId">The user's ID.</param>
 /// <param name="success">Callback triggers on successful data replacement.</param>
 public static Coroutine Replace(string ns, Hashtable data, int userId, dimeRocker.SuccessHandler success)
 {
     return ReplaceData(ns, data, userId, success, null);
 }
    /// <summary>
    /// Submits a score to the leaderboard.
    /// </summary>
    /// <param name="leaderboardName">The leaderboard to post the score to.</param>
    /// <param name="score">The user's score.</param>
    /// <param name="values">Arbitrary values to attach to the score (maximum three).</param>
    /// <param name="success">Callback triggers on successful data save.</param>
    /// <param name="error">Callback triggers on error.</param>
    public static Coroutine PostScore(string leaderboardName, int score, string[] values, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
    {
        drWWW www = new drWWW(drAPI.leaderboardPostScore);
        www.AddField("name", leaderboardName);
        www.AddField("score", score);

        if (values != null) {
            for (int i = 0; i < Mathf.Max(values.Length, 3); i++) {
                // Skip empty or null values
                if (values[i] == null || values[i] == "") {
                    continue;
                }

                www.AddField("v" + (i + 1), values[i]);
            }
        }

        www.OnSuccess += delegate {
            int rank = 0;
            int.TryParse(www.result.ToString(), out rank);

            if (rank == 0) {
                drDebug.LogWarning("User did not rank");
           		return;
            }

            drDebug.Log("Score posted to leaderboard " + leaderboardName + " with rank " + rank);
        }; www.OnSuccess += success;

        www.OnError += delegate (string errorMessage) {
            drDebug.LogError("Error posting score to leaderboard " + leaderboardName + ": " + errorMessage);
        }; www.OnError += error;

        return www.Fetch();
    }
    /// <summary>
    /// Fetches the leaderboard scores.
    /// </summary>
    /// <param name="leaderboardName">The name of the leaderboard.</param>
    /// <param name="offset">The entry to start from. A negative number will return from the end instead. Default is 0.</param>
    /// <param name="length">The number of entries to return. Default is 10.</param>
    /// <param name="success">Callback triggers on successful data save.</param>
    /// <param name="error">Callback triggers on error.</param>
    static Coroutine FetchLeaderboardEntries(string leaderboardName, int? offset, int? length, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
    {
        drWWW www = new drWWW(drAPI.leaderboardGet);
        www.AddField("name", leaderboardName);

        if (offset.HasValue) {
            www.AddField("offset", offset.Value);
        }

        if (length.HasValue) {
            www.AddField("length", length.Value);
        }

        www.OnSuccess += delegate {
            ArrayList result = www.result as ArrayList;
            List<Entry> entries = new List<Entry>();

            foreach (Hashtable entryData in result) {
                Entry entry = new Entry(entryData);
                entries.Add(entry);
            }

            Leaderboard lb = GetLeaderboard(leaderboardName);

            if (lb == null) {
                lb = new Leaderboard(leaderboardName);
                fetchedLeaderboards.Add(lb);
            }

            lb.AddEntries(offset.HasValue ? offset.Value + 1 : 1, entries.ToArray());
            drDebug.Log("Scores fetched from leaderboard " + leaderboardName);
        }; www.OnSuccess += success;

        www.OnError += delegate (string errorMessage) {
            drDebug.LogError("Error fetching scores from leaderboard " + leaderboardName + ": " + errorMessage);
        }; www.OnError += error;

        return www.Fetch();
    }
    /// <summary>
    /// Saves data.
    /// </summary>
    /// <param name="ns">The namespace of the data.</param>
    /// <param name="data">The data to save.</param>
    /// <param name="userId">The user's ID.</param>
    /// <param name="success">Callback triggers on successful data save.</param>
    /// <param name="error">Callback triggers on error.</param>
    static Coroutine SetData(string ns, Hashtable data, int? userId, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
    {
        drWWW www = GetWWW(drAPI.dataSet, ns, userId);

        string json = JSON.JsonEncode(data);
        www.AddField("data", json);

        Debug.Log("Data JSON: " + json);

        www.OnSuccess += delegate {
            int result = 0;
            int.TryParse(www.result.ToString(), out result);

            // This shouldn't happen, but the check is here just in case
            if (result != 1) {
                drDebug.LogWarning("No data saved");
                return;
            }

            StoreData(ns, data, userId);
            drDebug.Log("Data saved");
        }; www.OnSuccess += success;

        www.OnError += delegate (string errorMessage) {
            drDebug.LogError("Error saving data: " + errorMessage);
        }; www.OnError += error;

        return www.Fetch();
    }
    /// <summary>
    /// Unsets data.
    /// </summary>
    /// <param name="ns">The namespace of the data.</param>
    /// <param name="keys">Keys of the data to unset.</param>
    /// <param name="userId">The user's ID.</param>
    /// <param name="success">Callback triggers on successful data save.</param>
    /// <param name="error">Callback triggers on error.</param>
    static Coroutine UnsetData(string ns, string[] keys, int? userId, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
    {
        // Specifying the keys to unset is mandatory; to replace the entire document use ReplaceWith
        if (keys == null || keys.Length == 0) {
            drDebug.LogWarning("Keys must be specified when unsetting data");
            return null;
        }

        drWWW www = GetWWW(drAPI.dataUnset, ns, userId);

        string keysJson = JSON.JsonEncode(new ArrayList(keys));
        www.AddField("keys", keysJson);

        www.OnSuccess += delegate {
            int result = 0;
            int.TryParse(www.result.ToString(), out result);

            // This shouldn't happen, but the check is here just in case
            if (result != 1) {
                drDebug.LogWarning("Data not unset");
                return;
            }

            Data existing;

            if (userId.HasValue) {
                existing = GetData(ns, userId.Value);
            } else {
                existing = GetData(ns);
            }

            foreach (string key in keys) {
                existing.data.Remove(key);
            }

            drDebug.Log("Data unset successfully");
        }; www.OnSuccess += success;

        www.OnError += delegate (string errorMessage) {
            drDebug.LogError("Error unsetting data: " + errorMessage);
        }; www.OnError += error;

        return www.Fetch();
    }
 /// <summary>
 /// Loads global data.
 /// </summary>
 /// <param name="ns">The namespace of the data.</param>
 /// <param name="keys">Keys of the data to load.</param>
 /// <param name="success">Callback triggers on successful data save.</param>
 /// <param name="error">Callback triggers on error.</param>
 public static Coroutine Fetch(string ns, string[] keys, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
 {
     return FetchData(ns, keys, null, success, error);
 }
 /// <summary>
 /// Fetches the current Counter value for one or more users.
 /// </summary>
 /// <param name="name">The name of the counter.</param>
 /// <param name="userIds">The user's IDs.</param>
 /// <param name="success">Callback triggers on successful fetch.</param>
 public static Coroutine FetchCurrent(string name, int[] userIds, dimeRocker.SuccessHandler success)
 {
     return FetchCurrent(name, userIds, success, null);
 }
 /// <summary>
 /// Fetches the leaderboard scores.
 /// </summary>
 /// <param name="leaderboardName">The name of the leaderboard.</param>
 /// <param name="success">Callback triggers on successful data save.</param>
 /// <param name="error">Callback triggers on error.</param>
 public static Coroutine FetchEntries(string leaderboardName, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
 {
     return FetchLeaderboardEntries(leaderboardName, null, null, success, error);
 }
 /// <summary>
 /// Decrements the Counter value(s) for one or more users.
 /// </summary>
 /// <param name="name">The name of the counter.</param>
 /// <param name="amount">The amount to Decrement.</param>
 /// <param name="userIds">The user's IDs.</param>
 /// <param name="success">Callback triggers on successful Decrement.</param>
 public static Coroutine Decrement(string name, int amount, int[] userIds, dimeRocker.SuccessHandler success)
 {
     return Decrement(name, amount, userIds, success, null);
 }
 /// <summary>
 /// Fetches the leaderboard scores.
 /// </summary>
 /// <param name="leaderboardName">The name of the leaderboard.</param>
 /// <param name="offset">The entry to start from. A negative number will return from the end instead. Default is 0.</param>
 /// <param name="length">The number of entries to return. Default is 10.</param>
 /// <param name="success">Callback triggers on successful data save.</param>
 /// <param name="error">Callback triggers on error.</param>
 public static Coroutine FetchEntries(string leaderboardName, int offset, int length, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
 {
     return FetchLeaderboardEntries(leaderboardName, offset, length, success, error);
 }
    /// <summary>
    /// Debits the Wallet value(s) for one or more users.
    /// </summary>
    /// <param name="name">The name of the wallet.</param>
    /// <param name="amount">The amount to Debit.</param>
    /// <param name="description">A description of the transaction.</param>
    /// <param name="success">Callback triggers on successful debit.</param>
    /// <param name="error">Callback triggers on error.</param>
    public static Coroutine Debit(string name, int amount, string description, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
    {
        drWWW www = GetWWW(drAPI.walletDebit, name);
        www.AddField("amount", amount);

        if (description != null && description != "") {
            www.AddField("description", description);
        }

        www.OnSuccess += delegate {
            int result = 0;
            int.TryParse(www.result.ToString(), out result);
            StoreReturn(name, result);
            drDebug.Log("Debited wallet");
        }; www.OnSuccess += success;

        www.OnError += delegate (string errorMessage) {
            drDebug.LogError("Error debiting wallet " + name + ": " + errorMessage);
        }; www.OnError += error;

        return www.Fetch();
    }
 /// <summary>
 /// Submits a score to the leaderboard.
 /// </summary>
 /// <param name="leaderboardName">The leaderboard to post the score to.</param>
 /// <param name="score">The user's score.</param>
 /// <param name="success">Callback triggers on successful data save.</param>
 /// <param name="error">Callback triggers on error.</param>
 public static Coroutine PostScore(string leaderboardName, int score, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
 {
     return PostScore(leaderboardName, score, null, success, error);
 }
    /// <summary>
    /// Fetches the current Wallet value for one or more users.
    /// </summary>
    /// <param name="name">The name of the wallet.</param>
    /// <param name="success">Callback triggers on successful fetch.</param>
    /// <param name="error">Callback triggers on error.</param>
    public static Coroutine FetchBalance(string name, dimeRocker.SuccessHandler success, dimeRocker.ErrorHandler error)
    {
        drWWW www = GetWWW(drAPI.walletBalance, name);

        www.OnSuccess += delegate {
            int result = 0;
            int.TryParse(www.result.ToString(), out result);
            StoreReturn(name, result);
            drDebug.Log("Fetched wallet " + name + " balance");
        }; www.OnSuccess += success;

        www.OnError += delegate (string errorMessage) {
            drDebug.LogError("Error fetching wallet " + name + ": " + errorMessage);
        }; www.OnError += error;

        return www.Fetch();
    }
 /// <summary>
 /// Submits a score to the leaderboard.
 /// </summary>
 /// <param name="leaderboardName">The leaderboard to post the score to.</param>
 /// <param name="score">The user's score.</param>
 /// <param name="values">Arbitrary values to attach to the score (maximum three).</param>
 /// <param name="success">Callback triggers on successful data save.</param>
 public static Coroutine PostScore(string leaderboardName, int score, string[] values, dimeRocker.SuccessHandler success)
 {
     return PostScore(leaderboardName, score, values, success, null);
 }
 /// <summary>
 /// Credits the Wallet value(s) for one or more users.
 /// </summary>
 /// <param name="name">The name of the wallet.</param>
 /// <param name="amount">The amount to increment.</param>
 /// <param name="description">A description of the transaction.</param>
 /// <param name="success">Callback triggers on successful increment.</param>
 public static Coroutine Credit(string name, int amount, string description, dimeRocker.SuccessHandler success)
 {
     return Credit(name, amount, description, success, null);
 }
 /// <summary>
 /// Loads user-specific data.
 /// </summary>
 /// <param name="ns">The namespace of the data.</param>
 /// <param name="keys">Keys of the data to load.</param>
 /// <param name="userId">The user's ID.</param>
 /// <param name="success">Callback triggers on successful data save.</param>
 public static Coroutine Fetch(string ns, string[] keys, int userId, dimeRocker.SuccessHandler success)
 {
     return FetchData(ns, keys, userId, success, null);
 }