480 lines
19 KiB
C#
480 lines
19 KiB
C#
|
using System;
|
|||
|
using System.Threading.Tasks;
|
|||
|
using System.Collections.Generic;
|
|||
|
using LeanCloud.Storage.Internal;
|
|||
|
using System.IO;
|
|||
|
using System.Text;
|
|||
|
|
|||
|
namespace LeanCloud {
|
|||
|
/// <summary>
|
|||
|
/// 排行榜顺序
|
|||
|
/// </summary>
|
|||
|
public enum AVLeaderboardOrder {
|
|||
|
/// <summary>
|
|||
|
/// 升序
|
|||
|
/// </summary>
|
|||
|
ASCENDING,
|
|||
|
/// <summary>
|
|||
|
/// 降序
|
|||
|
/// </summary>
|
|||
|
DESCENDING
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 排行榜更新策略
|
|||
|
/// </summary>
|
|||
|
public enum AVLeaderboardUpdateStrategy {
|
|||
|
/// <summary>
|
|||
|
/// 更好地
|
|||
|
/// </summary>
|
|||
|
BETTER,
|
|||
|
/// <summary>
|
|||
|
/// 最近的
|
|||
|
/// </summary>
|
|||
|
LAST,
|
|||
|
/// <summary>
|
|||
|
/// 总和
|
|||
|
/// </summary>
|
|||
|
SUM,
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 排行榜刷新频率
|
|||
|
/// </summary>
|
|||
|
public enum AVLeaderboardVersionChangeInterval {
|
|||
|
/// <summary>
|
|||
|
/// 从不
|
|||
|
/// </summary>
|
|||
|
NEVER,
|
|||
|
/// <summary>
|
|||
|
/// 每天
|
|||
|
/// </summary>
|
|||
|
DAY,
|
|||
|
/// <summary>
|
|||
|
/// 每周
|
|||
|
/// </summary>
|
|||
|
WEEK,
|
|||
|
/// <summary>
|
|||
|
/// 每月
|
|||
|
/// </summary>
|
|||
|
MONTH
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 排行榜类
|
|||
|
/// </summary>
|
|||
|
public class AVLeaderboard {
|
|||
|
/// <summary>
|
|||
|
/// 成绩名字
|
|||
|
/// </summary>
|
|||
|
/// <value>The name of the statistic.</value>
|
|||
|
public string StatisticName {
|
|||
|
get; private set;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 排行榜顺序
|
|||
|
/// </summary>
|
|||
|
/// <value>The order.</value>
|
|||
|
public AVLeaderboardOrder Order {
|
|||
|
get; private set;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 排行榜更新策略
|
|||
|
/// </summary>
|
|||
|
/// <value>The update strategy.</value>
|
|||
|
public AVLeaderboardUpdateStrategy UpdateStrategy {
|
|||
|
get; private set;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 排行榜版本更新频率
|
|||
|
/// </summary>
|
|||
|
/// <value>The version change intervak.</value>
|
|||
|
public AVLeaderboardVersionChangeInterval VersionChangeInterval {
|
|||
|
get; private set;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 版本号
|
|||
|
/// </summary>
|
|||
|
/// <value>The version.</value>
|
|||
|
public int Version {
|
|||
|
get; private set;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 下次重置时间
|
|||
|
/// </summary>
|
|||
|
/// <value>The next reset at.</value>
|
|||
|
public DateTime NextResetAt {
|
|||
|
get; private set;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 创建时间
|
|||
|
/// </summary>
|
|||
|
/// <value>The created at.</value>
|
|||
|
public DateTime CreatedAt {
|
|||
|
get; private set;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Leaderboard 构造方法
|
|||
|
/// </summary>
|
|||
|
/// <param name="statisticName">成绩名称</param>
|
|||
|
AVLeaderboard(string statisticName) {
|
|||
|
StatisticName = statisticName;
|
|||
|
}
|
|||
|
|
|||
|
AVLeaderboard() {
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 创建排行榜对象
|
|||
|
/// </summary>
|
|||
|
/// <returns>排行榜对象</returns>
|
|||
|
/// <param name="statisticName">名称</param>
|
|||
|
/// <param name="order">排序方式</param>
|
|||
|
/// <param name="versionChangeInterval">版本更新频率</param>
|
|||
|
/// <param name="updateStrategy">成绩更新策略</param>
|
|||
|
public static Task<AVLeaderboard> CreateLeaderboard(string statisticName,
|
|||
|
AVLeaderboardOrder order = AVLeaderboardOrder.DESCENDING,
|
|||
|
AVLeaderboardUpdateStrategy updateStrategy = AVLeaderboardUpdateStrategy.BETTER,
|
|||
|
AVLeaderboardVersionChangeInterval versionChangeInterval = AVLeaderboardVersionChangeInterval.WEEK) {
|
|||
|
|
|||
|
if (string.IsNullOrEmpty(statisticName)) {
|
|||
|
throw new ArgumentNullException(nameof(statisticName));
|
|||
|
}
|
|||
|
var data = new Dictionary<string, object> {
|
|||
|
{ "statisticName", statisticName },
|
|||
|
{ "order", order.ToString().ToLower() },
|
|||
|
{ "versionChangeInterval", versionChangeInterval.ToString().ToLower() },
|
|||
|
{ "updateStrategy", updateStrategy.ToString().ToLower() },
|
|||
|
};
|
|||
|
var command = new AVCommand("leaderboard/leaderboards", "POST", data: data);
|
|||
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
|
|||
|
try {
|
|||
|
var leaderboard = Parse(t.Result.Item2);
|
|||
|
return leaderboard;
|
|||
|
} catch (Exception e) {
|
|||
|
throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 创建排行榜对象
|
|||
|
/// </summary>
|
|||
|
/// <returns>排行榜对象</returns>
|
|||
|
/// <param name="statisticName">名称</param>
|
|||
|
public static AVLeaderboard CreateWithoutData(string statisticName) {
|
|||
|
if (string.IsNullOrEmpty(statisticName)) {
|
|||
|
throw new ArgumentNullException(nameof(statisticName));
|
|||
|
}
|
|||
|
return new AVLeaderboard(statisticName);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取排行榜对象
|
|||
|
/// </summary>
|
|||
|
/// <returns>排行榜对象</returns>
|
|||
|
/// <param name="statisticName">名称</param>
|
|||
|
public static Task<AVLeaderboard> GetLeaderboard(string statisticName) {
|
|||
|
return CreateWithoutData(statisticName).Fetch();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 更新用户成绩
|
|||
|
/// </summary>
|
|||
|
/// <returns>更新的成绩</returns>
|
|||
|
/// <param name="user">用户</param>
|
|||
|
/// <param name="statistics">成绩</param>
|
|||
|
/// <param name="overwrite">是否强行覆盖</param>
|
|||
|
public static Task<List<AVStatistic>> UpdateStatistics(AVUser user, Dictionary<string, double> statistics, bool overwrite = false) {
|
|||
|
if (user == null) {
|
|||
|
throw new ArgumentNullException(nameof(user));
|
|||
|
}
|
|||
|
if (statistics == null || statistics.Count == 0) {
|
|||
|
throw new ArgumentNullException(nameof(statistics));
|
|||
|
}
|
|||
|
var data = new List<object>();
|
|||
|
foreach (var statistic in statistics) {
|
|||
|
var kv = new Dictionary<string, object> {
|
|||
|
{ "statisticName", statistic.Key },
|
|||
|
{ "statisticValue", statistic.Value },
|
|||
|
};
|
|||
|
data.Add(kv);
|
|||
|
}
|
|||
|
var path = string.Format("leaderboard/users/{0}/statistics", user.ObjectId);
|
|||
|
if (overwrite) {
|
|||
|
path = string.Format("{0}?overwrite=1", path);
|
|||
|
}
|
|||
|
var dataStr = Json.Encode(data);
|
|||
|
var dataStream = new MemoryStream(Encoding.UTF8.GetBytes(dataStr));
|
|||
|
var command = new AVCommand(path, "POST", contentType: "application/json", sessionToken: user.SessionToken, stream: dataStream);
|
|||
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
|
|||
|
try {
|
|||
|
List<AVStatistic> statisticList = new List<AVStatistic>();
|
|||
|
List<object> list = t.Result.Item2["results"] as List<object>;
|
|||
|
foreach (object obj in list) {
|
|||
|
statisticList.Add(AVStatistic.Parse(obj as IDictionary<string, object>));
|
|||
|
}
|
|||
|
return statisticList;
|
|||
|
} catch (Exception e) {
|
|||
|
throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取用户成绩
|
|||
|
/// </summary>
|
|||
|
/// <returns>成绩列表</returns>
|
|||
|
/// <param name="user">用户</param>
|
|||
|
/// <param name="statisticNames">名称列表</param>
|
|||
|
public static Task<List<AVStatistic>> GetStatistics(AVUser user, List<string> statisticNames = null) {
|
|||
|
if (user == null) {
|
|||
|
throw new ArgumentNullException(nameof(user));
|
|||
|
}
|
|||
|
var path = string.Format("leaderboard/users/{0}/statistics", user.ObjectId);
|
|||
|
if (statisticNames != null && statisticNames.Count > 0) {
|
|||
|
var names = string.Join(",", statisticNames.ToArray());
|
|||
|
path = string.Format("{0}?statistics={1}", path, names);
|
|||
|
}
|
|||
|
var sessionToken = AVUser.CurrentUser?.SessionToken;
|
|||
|
var command = new AVCommand(path, "GET", sessionToken, data: null);
|
|||
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
|
|||
|
try {
|
|||
|
List<AVStatistic> statisticList = new List<AVStatistic>();
|
|||
|
List<object> list = t.Result.Item2["results"] as List<object>;
|
|||
|
foreach (object obj in list) {
|
|||
|
statisticList.Add(AVStatistic.Parse(obj as IDictionary<string, object>));
|
|||
|
}
|
|||
|
return statisticList;
|
|||
|
} catch (Exception e) {
|
|||
|
throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 删除用户成绩
|
|||
|
/// </summary>
|
|||
|
/// <param name="user">用户</param>
|
|||
|
/// <param name="statisticNames">名称列表</param>
|
|||
|
public static Task DeleteStatistics(AVUser user, List<string> statisticNames) {
|
|||
|
if (user == null) {
|
|||
|
throw new ArgumentNullException(nameof(user));
|
|||
|
}
|
|||
|
if (statisticNames == null || statisticNames.Count == 0) {
|
|||
|
throw new ArgumentNullException(nameof(statisticNames));
|
|||
|
}
|
|||
|
var path = string.Format("leaderboard/users/{0}/statistics", user.ObjectId);
|
|||
|
var names = string.Join(",", statisticNames.ToArray());
|
|||
|
path = string.Format("{0}?statistics={1}", path, names);
|
|||
|
var command = new AVCommand(path, "DELETE", sessionToken: user.SessionToken, data: null);
|
|||
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取排行榜历史数据
|
|||
|
/// </summary>
|
|||
|
/// <returns>排行榜归档列表</returns>
|
|||
|
/// <param name="skip">跳过数量</param>
|
|||
|
/// <param name="limit">分页数量</param>
|
|||
|
public Task<List<AVLeaderboardArchive>> GetArchives(int skip = 0, int limit = 10) {
|
|||
|
var path = string.Format("leaderboard/leaderboards/{0}/archives", StatisticName);
|
|||
|
path = string.Format("{0}?skip={1}&limit={2}", path, skip, limit);
|
|||
|
var command = new AVCommand(path, "GET", data: null);
|
|||
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
|
|||
|
List<AVLeaderboardArchive> archives = new List<AVLeaderboardArchive>();
|
|||
|
List<object> list = t.Result.Item2["results"] as List<object>;
|
|||
|
foreach (object obj in list) {
|
|||
|
archives.Add(AVLeaderboardArchive.Parse(obj as IDictionary<string, object>));
|
|||
|
}
|
|||
|
return archives;
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取排行榜结果
|
|||
|
/// </summary>
|
|||
|
/// <returns>排名列表</returns>
|
|||
|
public Task<List<AVRanking>> GetResults(int version = -1, int skip = 0, int limit = 10, List<string> selectUserKeys = null,
|
|||
|
List<string> includeStatistics = null) {
|
|||
|
return GetResults(null, version, skip, limit, selectUserKeys, includeStatistics);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取用户及附近的排名
|
|||
|
/// </summary>
|
|||
|
/// <returns>排名列表</returns>
|
|||
|
/// <param name="user">用户</param>
|
|||
|
/// <param name="version">版本号</param>
|
|||
|
/// <param name="skip">跳过数量</param>
|
|||
|
/// <param name="limit">分页数量</param>
|
|||
|
/// <param name="selectUserKeys">包含的玩家的字段列表</param>
|
|||
|
/// <param name="includeStatistics">包含的其他排行榜名称</param>
|
|||
|
public Task<List<AVRanking>> GetResultsAroundUser(int version = -1, int skip = 0, int limit = 10,
|
|||
|
List<string> selectUserKeys = null,
|
|||
|
List<string> includeStatistics = null) {
|
|||
|
return GetResults(AVUser.CurrentUser, version, skip, limit, selectUserKeys, includeStatistics);
|
|||
|
}
|
|||
|
|
|||
|
Task<List<AVRanking>> GetResults(AVUser user,
|
|||
|
int version, int skip, int limit,
|
|||
|
List<string> selectUserKeys,
|
|||
|
List<string> includeStatistics) {
|
|||
|
|
|||
|
var path = string.Format("leaderboard/leaderboards/{0}/ranks", StatisticName);
|
|||
|
if (user != null) {
|
|||
|
path = string.Format("{0}/{1}", path, user.ObjectId);
|
|||
|
}
|
|||
|
path = string.Format("{0}?skip={1}&limit={2}", path, skip, limit);
|
|||
|
if (version != -1) {
|
|||
|
path = string.Format("{0}&version={1}", path, version);
|
|||
|
}
|
|||
|
if (selectUserKeys != null) {
|
|||
|
var keys = string.Join(",", selectUserKeys.ToArray());
|
|||
|
path = string.Format("{0}&includeUser={1}", path, keys);
|
|||
|
}
|
|||
|
if (includeStatistics != null) {
|
|||
|
var statistics = string.Join(",", includeStatistics.ToArray());
|
|||
|
path = string.Format("{0}&includeStatistics={1}", path, statistics);
|
|||
|
}
|
|||
|
var command = new AVCommand(path, "GET", data: null);
|
|||
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
|
|||
|
try {
|
|||
|
List<AVRanking> rankingList = new List<AVRanking>();
|
|||
|
List<object> list = t.Result.Item2["results"] as List<object>;
|
|||
|
foreach (object obj in list) {
|
|||
|
rankingList.Add(AVRanking.Parse(obj as IDictionary<string, object>));
|
|||
|
}
|
|||
|
return rankingList;
|
|||
|
} catch (Exception e) {
|
|||
|
throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 设置更新策略
|
|||
|
/// </summary>
|
|||
|
/// <returns>排行榜对象</returns>
|
|||
|
/// <param name="updateStrategy">更新策略</param>
|
|||
|
public Task<AVLeaderboard> UpdateUpdateStrategy(AVLeaderboardUpdateStrategy updateStrategy) {
|
|||
|
var data = new Dictionary<string, object> {
|
|||
|
{ "updateStrategy", updateStrategy.ToString().ToLower() }
|
|||
|
};
|
|||
|
return Update(data).OnSuccess(t => {
|
|||
|
UpdateStrategy = (AVLeaderboardUpdateStrategy)Enum.Parse(typeof(AVLeaderboardUpdateStrategy), t.Result["updateStrategy"].ToString().ToUpper());
|
|||
|
return this;
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 设置版本更新频率
|
|||
|
/// </summary>
|
|||
|
/// <returns>排行榜对象</returns>
|
|||
|
/// <param name="versionChangeInterval">版本更新频率</param>
|
|||
|
public Task<AVLeaderboard> UpdateVersionChangeInterval(AVLeaderboardVersionChangeInterval versionChangeInterval) {
|
|||
|
var data = new Dictionary<string, object> {
|
|||
|
{ "versionChangeInterval", versionChangeInterval.ToString().ToLower() }
|
|||
|
};
|
|||
|
return Update(data).OnSuccess(t => {
|
|||
|
VersionChangeInterval = (AVLeaderboardVersionChangeInterval)Enum.Parse(typeof(AVLeaderboardVersionChangeInterval), t.Result["versionChangeInterval"].ToString().ToUpper());
|
|||
|
return this;
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
Task<IDictionary<string,object>> Update(Dictionary<string, object> data) {
|
|||
|
var path = string.Format("leaderboard/leaderboards/{0}", StatisticName);
|
|||
|
var command = new AVCommand(path, "PUT", data: data);
|
|||
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
|
|||
|
return t.Result.Item2;
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 拉取排行榜数据
|
|||
|
/// </summary>
|
|||
|
/// <returns>排行榜对象</returns>
|
|||
|
public Task<AVLeaderboard> Fetch() {
|
|||
|
var path = string.Format("leaderboard/leaderboards/{0}", StatisticName);
|
|||
|
var command = new AVCommand(path, "GET", data: null);
|
|||
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
|
|||
|
try {
|
|||
|
// 反序列化 Leaderboard 对象
|
|||
|
var leaderboard = Parse(t.Result.Item2);
|
|||
|
return leaderboard;
|
|||
|
} catch (Exception e) {
|
|||
|
throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 重置排行榜
|
|||
|
/// </summary>
|
|||
|
/// <returns>排行榜对象</returns>
|
|||
|
public Task<AVLeaderboard> Reset() {
|
|||
|
var path = string.Format("leaderboard/leaderboards/{0}/incrementVersion", StatisticName);
|
|||
|
var command = new AVCommand(path, "PUT", data: null);
|
|||
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
|
|||
|
try {
|
|||
|
Init(t.Result.Item2);
|
|||
|
return this;
|
|||
|
} catch (Exception e) {
|
|||
|
throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 销毁排行榜
|
|||
|
/// </summary>
|
|||
|
public Task Destroy() {
|
|||
|
var path = string.Format("leaderboard/leaderboards/{0}", StatisticName);
|
|||
|
var command = new AVCommand(path, "DELETE", data: null);
|
|||
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command);
|
|||
|
}
|
|||
|
|
|||
|
static AVLeaderboard Parse(IDictionary<string, object> data) {
|
|||
|
if (data == null) {
|
|||
|
throw new ArgumentNullException(nameof(data));
|
|||
|
}
|
|||
|
var leaderboard = new AVLeaderboard();
|
|||
|
leaderboard.Init(data);
|
|||
|
return leaderboard;
|
|||
|
}
|
|||
|
|
|||
|
void Init(IDictionary<string, object> data) {
|
|||
|
if (data == null) {
|
|||
|
throw new ArgumentNullException(nameof(data));
|
|||
|
}
|
|||
|
object nameObj;
|
|||
|
if (data.TryGetValue("statisticName", out nameObj)) {
|
|||
|
StatisticName = nameObj.ToString();
|
|||
|
}
|
|||
|
object orderObj;
|
|||
|
if (data.TryGetValue("order", out orderObj)) {
|
|||
|
Order = (AVLeaderboardOrder)Enum.Parse(typeof(AVLeaderboardOrder), orderObj.ToString().ToUpper());
|
|||
|
}
|
|||
|
object strategyObj;
|
|||
|
if (data.TryGetValue("updateStrategy", out strategyObj)) {
|
|||
|
UpdateStrategy = (AVLeaderboardUpdateStrategy)Enum.Parse(typeof(AVLeaderboardUpdateStrategy), strategyObj.ToString().ToUpper());
|
|||
|
}
|
|||
|
object intervalObj;
|
|||
|
if (data.TryGetValue("versionChangeInterval", out intervalObj)) {
|
|||
|
VersionChangeInterval = (AVLeaderboardVersionChangeInterval)Enum.Parse(typeof(AVLeaderboardVersionChangeInterval), intervalObj.ToString().ToUpper());
|
|||
|
}
|
|||
|
object versionObj;
|
|||
|
if (data.TryGetValue("version", out versionObj)) {
|
|||
|
Version = int.Parse(versionObj.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|