1217 lines
28 KiB
C#
1217 lines
28 KiB
C#
![]() |
// ***********************************************************************
|
|||
|
// Assembly : Game
|
|||
|
// Author : Kimch
|
|||
|
// Created : 2021-05-01
|
|||
|
// Description :
|
|||
|
// Last Modified By :
|
|||
|
// Last Modified On :
|
|||
|
// ***********************************************************************
|
|||
|
// <copyright file= "ServerProxy" company="Kunpo"></copyright>
|
|||
|
// <summary></summary>
|
|||
|
// ***********************************************************************
|
|||
|
#if UNITY_EDITOR || DEBUG_MY
|
|||
|
#define TEST_SERVER
|
|||
|
#endif
|
|||
|
|
|||
|
using F;
|
|||
|
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
using UnityEngine;
|
|||
|
using UnityEngine.Networking;
|
|||
|
|
|||
|
namespace G
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// 服务器
|
|||
|
/// </summary>
|
|||
|
public class ServerProxy : F.GameProxy
|
|||
|
{
|
|||
|
public class ServerTask
|
|||
|
{
|
|||
|
public string taskName;
|
|||
|
public object taskData;
|
|||
|
}
|
|||
|
|
|||
|
#region Field
|
|||
|
|
|||
|
private string uid
|
|||
|
{
|
|||
|
get => UserProxy.AccountID;
|
|||
|
set => UserProxy.AccountID = value;
|
|||
|
}
|
|||
|
|
|||
|
private string token
|
|||
|
{
|
|||
|
get => UserProxy.AccountToken;
|
|||
|
set => UserProxy.AccountToken = value;
|
|||
|
}
|
|||
|
|
|||
|
public string openId
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
public static System.Action LoginSuccessEvent;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Property
|
|||
|
|
|||
|
public bool isSdkLoginSuccess
|
|||
|
{
|
|||
|
get;
|
|||
|
set;
|
|||
|
}
|
|||
|
|
|||
|
public bool isLoginSuccess
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
public int loginStatus
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
public bool getUserInfoSuccess
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
public int banStatus
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
public string banMessage
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Method
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="openId"></param>
|
|||
|
/// <param name="openToken"></param>
|
|||
|
/// <param name="loginType">通过游客方式登录 LOGIN_TYPE_VISITOR = 1</param>
|
|||
|
/// <param name="loginInvokeMode">// 正常登陆 NormalLogin = 1,// 账号绑定 AccountBind = 2,// 切换账号 SwitchAccount = 3,</param>
|
|||
|
public void OnSDKLogin(string openId, string openToken, int loginType, int loginInvokeMode)
|
|||
|
{
|
|||
|
isLoginSuccess = false;
|
|||
|
Login();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="error"></param>
|
|||
|
/// <param name="errorMsg"></param>
|
|||
|
/// <param name="apiLoginType"></param>
|
|||
|
public void OnSDKLoginFail(int error, string errorMsg, int apiLoginType)
|
|||
|
{
|
|||
|
PostNotification(GlobalDefine.EVENT_SHOW_TOAST, "帐号登录失败,请重新登录");
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
private bool CheckLogin()
|
|||
|
{
|
|||
|
if (string.IsNullOrEmpty(KPlatform.Instance.openId))
|
|||
|
{
|
|||
|
PostNotification(GlobalDefine.EVENT_SHOW_TOAST, "帐号登录失败,请重新登录");
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
if (!isLoginSuccess)
|
|||
|
{
|
|||
|
PostNotification(GlobalDefine.EVENT_SHOW_TOAST, "帐号未登录,请检查网络");
|
|||
|
return false;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
public void Login()
|
|||
|
{
|
|||
|
// 临时登录
|
|||
|
var dataTT = new Dictionary<string, object>();
|
|||
|
OnLoginSuccess(dataTT);
|
|||
|
loginStatus = 1;
|
|||
|
return;
|
|||
|
|
|||
|
loginStatus = 0;
|
|||
|
|
|||
|
_jsonData.Clear();
|
|||
|
_jsonData.Add("device_id", KPlatform.Instance.deviceId);
|
|||
|
|
|||
|
#if !UNITY_EDITOR && SDK_WECHAT_WASM
|
|||
|
if (!string.IsNullOrEmpty(KPlatform.Instance.openId))
|
|||
|
_jsonData.Add("js_code", KPlatform.Instance.openId);
|
|||
|
#else
|
|||
|
_jsonData.Add("sdkid", KPlatform.Instance.openId);
|
|||
|
if (!string.IsNullOrEmpty(KPlatform.Instance.openToken))
|
|||
|
_jsonData.Add("token", KPlatform.Instance.openToken);
|
|||
|
#endif
|
|||
|
_jsonData.Add("channel", KPlatform.Instance.channel);
|
|||
|
_jsonData.Add("sub_channel", KPlatform.Instance.subChanel);
|
|||
|
_jsonData.Add("plat", KPlatform.Instance.platform);
|
|||
|
|
|||
|
_jsonData.Add("version", Application.version);
|
|||
|
|
|||
|
|
|||
|
var body = F.Utils.ToJsonBytes(_jsonData);
|
|||
|
StartCoroutine(Request("/api/user/auth", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
var dataT = table.GetDictionary("data");
|
|||
|
this.OnLoginSuccess(dataT);
|
|||
|
loginStatus = 1;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
loginStatus = 2;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
loginStatus = 3;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="data"></param>
|
|||
|
private void OnLoginSuccess(IDictionary<string, object> data)
|
|||
|
{
|
|||
|
if (data != null)
|
|||
|
{
|
|||
|
uid = data.GetString("uid");
|
|||
|
token = data.GetString("token");
|
|||
|
|
|||
|
UserProxy.UserId = data.GetString("userid");
|
|||
|
this.openId = data.GetString("sdkid");
|
|||
|
|
|||
|
var timestamp = data.GetInt("timestamp");
|
|||
|
AntiCheatProxy.Instance.SetServerTimestamp(timestamp);
|
|||
|
Launch.SetServerTimestamp(timestamp);
|
|||
|
|
|||
|
this.isLoginSuccess = true;
|
|||
|
banStatus = data.GetInt("ban_status");
|
|||
|
if (banStatus != 0)
|
|||
|
{
|
|||
|
var banReason = data.GetString("ban_record");
|
|||
|
var banTimestamp = data.GetInt("open_time");
|
|||
|
banMessage = "封禁帐号\n" + UserProxy.UserId + "\n" + banReason + "\n封禁截止" + F.Utils.Time.ToDataTime(banTimestamp).ToLocalTime().ToString();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
LoginSuccessEvent?.Invoke();
|
|||
|
LoginSuccessEvent = null;
|
|||
|
}
|
|||
|
if (KPlatform.Instance.loginInvokeMode != 1)
|
|||
|
ArchiveProxy.Instance.OnServerLogin(KPlatform.Instance.loginInvokeMode);
|
|||
|
}
|
|||
|
|
|||
|
//StartCoroutine(ScheduleCO());
|
|||
|
#if UNITY_EDITOR
|
|||
|
StartCoroutine(TestCO());
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
IEnumerator ScheduleCO()
|
|||
|
{
|
|||
|
yield return new WaitForSeconds(1f);
|
|||
|
KPlatform.Instance.GetUserNickName((error, message) =>
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
UpdateUserInfo(PlayerProxy.Instance.socialNickName, PlayerProxy.Instance.socialAvatarUrl);
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
yield return new WaitForSeconds(1f);
|
|||
|
var inviteInfo = KPlatform.Instance.inviteInfo;
|
|||
|
if (inviteInfo != null)
|
|||
|
{
|
|||
|
Invite(inviteInfo.userId, (error, message) =>
|
|||
|
{
|
|||
|
if (error != ErrorCode.NETWORK_ERROR)
|
|||
|
KPlatform.Instance.inviteInfo = null;
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
IEnumerator TestCO()
|
|||
|
{
|
|||
|
yield return new WaitForSeconds(5f);
|
|||
|
GetInviteCount(null);
|
|||
|
yield return new WaitForSeconds(1f);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取用户信息
|
|||
|
/// </summary>
|
|||
|
/// <param name="callback"></param>
|
|||
|
public void GetUserInfo(Callback3 callback)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData();
|
|||
|
jsonData.Add("nick", PlayerProxy.Instance.nickName);
|
|||
|
jsonData.Add("avatar", PlayerProxy.Instance.headURL);
|
|||
|
jsonData.Add("lv", PlayerProxy.Instance.grade);
|
|||
|
jsonData.Add("bat_power", PlayerProxy.Instance.combatValue);
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/user/update_user", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
callback?.Invoke(0, "", null);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
callback?.Invoke(errorCode, errorMsg, null);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
callback?.Invoke(error, data?.ToString(), null);
|
|||
|
}
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="errorCode"></param>
|
|||
|
/// <param name="errorMsg"></param>
|
|||
|
private void OnNetworkError(int errorCode, string errorMsg)
|
|||
|
{
|
|||
|
PostNotification(GlobalDefine.EVENT_SHOW_TOAST, errorMsg);
|
|||
|
Debug.Log($"网络错误 {errorCode} {errorMsg}");
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="errorCode"></param>
|
|||
|
/// <param name="errorMsg"></param>
|
|||
|
private void OnServerError(int errorCode, string errorMsg)
|
|||
|
{
|
|||
|
PostNotification(GlobalDefine.EVENT_SHOW_TOAST, errorMsg);
|
|||
|
Debug.Log($"服务器错误 {errorCode} {errorMsg}");
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="showToast"></param>
|
|||
|
/// <param name="callback"></param>
|
|||
|
public void UploadArchive(string archiveInfo, string archive, string archiveInfo2, string archive2, Callback2 callback)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData();
|
|||
|
var archives = new List<object>(4);
|
|||
|
if (archiveInfo != null)
|
|||
|
archives.Add(new Dictionary<string, object>
|
|||
|
{
|
|||
|
{ "name", "archive_info" },
|
|||
|
{ "data",archiveInfo}
|
|||
|
});
|
|||
|
if (archive != null)
|
|||
|
archives.Add(new Dictionary<string, object>
|
|||
|
{
|
|||
|
{ "name", "archive" },
|
|||
|
{ "data",archive}
|
|||
|
});
|
|||
|
if (archiveInfo2 != null)
|
|||
|
archives.Add(new Dictionary<string, object>
|
|||
|
{
|
|||
|
{ "name", "archive2_info" },
|
|||
|
{ "data",archiveInfo2}
|
|||
|
});
|
|||
|
if (archive2 != null)
|
|||
|
archives.Add(new Dictionary<string, object>
|
|||
|
{
|
|||
|
{ "name", "archive2" },
|
|||
|
{ "data",archive2}
|
|||
|
});
|
|||
|
|
|||
|
jsonData.Add("archives", archives);
|
|||
|
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/archive/upload", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
Debug.Log("上传存档");
|
|||
|
callback?.Invoke(0, null);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
callback?.Invoke(errorCode, errorMsg);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
callback?.Invoke(error, data?.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
public void DownloadArchive(string[] archiveKeys, Callback3 callback = null)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData();
|
|||
|
|
|||
|
if (archiveKeys == null)
|
|||
|
archiveKeys = new string[] { "archive_info", "archive" };
|
|||
|
jsonData.Add("tb_names", archiveKeys);
|
|||
|
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/archive/get", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
var dataT = table.GetDictionary("data");
|
|||
|
if (dataT != null)
|
|||
|
{
|
|||
|
var archives = dataT.GetList("archives");
|
|||
|
callback?.Invoke(0, "", archives);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
callback?.Invoke(0, "", null);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
callback?.Invoke(error, errorMsg, null);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
callback?.Invoke(error, data?.ToString(), null);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="rank_id"></param>
|
|||
|
/// <param name="score"></param>
|
|||
|
/// <param name="user_key"></param>
|
|||
|
public void UploadScore(int rank_id, int score, string user_key)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData();
|
|||
|
jsonData.Add("rank_id", rank_id);
|
|||
|
jsonData.Add("score", score);
|
|||
|
jsonData.Add("user_key", user_key);
|
|||
|
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/rank/update", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="rank_data"></param>
|
|||
|
/// <param name="user_key"></param>
|
|||
|
public void UploadScore(object rank_data, string user_key)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData();
|
|||
|
jsonData.Add("user_key", user_key);
|
|||
|
jsonData.Add("rank_data", rank_data);
|
|||
|
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/rank/batch_update", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="rank_id"></param>
|
|||
|
/// <param name="callback"></param>
|
|||
|
public void PullRanking(int rank_id, Callback2 callback)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData();
|
|||
|
jsonData.Add("rank_id", rank_id);
|
|||
|
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/rank/load", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
var dataT = table.GetDictionary("data");
|
|||
|
if (dataT != null)
|
|||
|
{
|
|||
|
var packet = new GamePacket
|
|||
|
{
|
|||
|
id = rank_id,
|
|||
|
data = dataT.GetList("list"),
|
|||
|
};
|
|||
|
RankingProxy.Instance.Recv(packet);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
}
|
|||
|
callback(error, "");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="code"></param>
|
|||
|
public void RedeemCode(string code)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData(true);
|
|||
|
jsonData.Add("cdkey", code);
|
|||
|
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/api/exchange_cdkey", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
var dataT = table.GetDictionary("data");
|
|||
|
|
|||
|
var text = dataT.GetString("gifts");
|
|||
|
var gifts = text.Split(',', '|');
|
|||
|
var results = F.ListPool<Item.ItemInfo>.Get();
|
|||
|
for (int i = 0; i < gifts.Length;)
|
|||
|
{
|
|||
|
int.TryParse(gifts[i++], out int id);
|
|||
|
int.TryParse(gifts[i++], out int count);
|
|||
|
if (id > 0)
|
|||
|
results.Add(new Item.ItemInfo
|
|||
|
{
|
|||
|
id = id,
|
|||
|
count = count,
|
|||
|
});
|
|||
|
}
|
|||
|
RewardProxy.Instance.GetRewardsWithUI(results, null);
|
|||
|
F.ListPool<Item.ItemInfo>.Release(results);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
PostNotification(GlobalDefine.EVENT_SHOW_TOAST, errorMsg);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 拉取邮件
|
|||
|
/// </summary>
|
|||
|
/// <param name="code"></param>
|
|||
|
/// <param name="callback"></param>
|
|||
|
public void PullMails(Callback2 callback)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData();
|
|||
|
//jsonData.Add("max_mail_id", 10);
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/mail/load", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
var dataT = table.GetDictionary("data");
|
|||
|
var mailList = dataT.GetList("infos");
|
|||
|
|
|||
|
// id title content rewards
|
|||
|
var gp = new GamePacket
|
|||
|
{
|
|||
|
id = GlobalDefine.PACKET_ID_MAIL_GET_ALL,
|
|||
|
data = mailList
|
|||
|
};
|
|||
|
PostNotification(GlobalDefine.EVENT_NETWORK_PACKET, gp, "server");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
PostNotification(GlobalDefine.EVENT_SHOW_TOAST, errorMsg);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="mailIds"></param>
|
|||
|
/// <param name="callback"></param>
|
|||
|
public void DeleteMails(string[] mailIds, Callback2 callback)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData();
|
|||
|
jsonData.Add("ids", mailIds);
|
|||
|
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/mail/delete", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
var dataT = table.GetDictionary("data");
|
|||
|
|
|||
|
callback?.Invoke(0, "");
|
|||
|
// id title content rewards
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
callback?.Invoke(errorCode, errorMsg);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="mailIds"></param>
|
|||
|
/// <param name="callback"></param>
|
|||
|
public void CreateOrder(string product_id, int product_count, string product_name, Callback3 callback)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData(true);
|
|||
|
jsonData.Add("product_id", product_id);
|
|||
|
jsonData.Add("product_count", product_count);
|
|||
|
jsonData.Add("product_name", product_name);
|
|||
|
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/order/create", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
var dataT = table.GetDictionary("data");
|
|||
|
callback?.Invoke(0, "", dataT);
|
|||
|
// id title content rewards
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
callback?.Invoke(errorCode, errorMsg, null);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="callback"></param>
|
|||
|
public void GetOrderResult(Callback3 callback)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData(true);
|
|||
|
jsonData.Add("sdkid", this.openId);
|
|||
|
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/order/load_shopping_list", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
var dataT = table.GetDictionary("data");
|
|||
|
var itemList = dataT.GetList("list");
|
|||
|
//for (int i = 0; i < itemList.Count; i++)
|
|||
|
//{
|
|||
|
// var itemInfo = itemList.GetDictionary(i);
|
|||
|
// var id = itemInfo.GetString("id");
|
|||
|
// var count = itemInfo.GetInt("count");
|
|||
|
//}
|
|||
|
|
|||
|
callback?.Invoke(0, "", itemList);
|
|||
|
// id title content rewards
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
callback?.Invoke(errorCode, errorMsg, null);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
public void Invite(string invit_userid, Callback2 callback)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData();
|
|||
|
jsonData.Add("invit_userid", invit_userid);
|
|||
|
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/invit/agree", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
var dataT = table.GetDictionary("data");
|
|||
|
callback?.Invoke(0, "");
|
|||
|
// id title content rewards
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
//OnServerError(errorCode, errorMsg);
|
|||
|
callback?.Invoke(errorCode, errorMsg);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//OnNetworkError(error, data?.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="callback"></param>
|
|||
|
public void GetInviteCount(Callback2 callback)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData();
|
|||
|
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/invit/get_invit_count", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
var dataT = table.GetDictionary("data");
|
|||
|
int count = dataT.GetInt("count");
|
|||
|
|
|||
|
var gp = new GamePacket
|
|||
|
{
|
|||
|
id = GlobalDefine.PACKET_ID_GET_INVITE_COUNT,
|
|||
|
data = count
|
|||
|
};
|
|||
|
InviteProxy.Instance.Recv(gp);
|
|||
|
//SendNotification(GlobalDefine.EVENT_NETWORK_PACKET, gp, "server");
|
|||
|
callback?.Invoke(0, "");
|
|||
|
// id title content rewards
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
callback?.Invoke(errorCode, errorMsg);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="callback"></param>
|
|||
|
public void GetInviteList(Callback2 callback)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData();
|
|||
|
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/invit/list", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
var dataT = table.GetDictionary("data");
|
|||
|
|
|||
|
var invited_list = dataT.GetList("invited_list");
|
|||
|
|
|||
|
var gp = new GamePacket
|
|||
|
{
|
|||
|
id = GlobalDefine.PACKET_ID_GET_INVITE_LIST,
|
|||
|
data = invited_list
|
|||
|
};
|
|||
|
InviteProxy.Instance.Recv(gp);
|
|||
|
//SendNotification(GlobalDefine.EVENT_NETWORK_PACKET, gp, "server");
|
|||
|
callback?.Invoke(0, "");
|
|||
|
// id title content rewards
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
callback?.Invoke(errorCode, errorMsg);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="callback"></param>
|
|||
|
public void GetTimestamp(Callback2 callback)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData();
|
|||
|
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/api/timestamp", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
var dataT = table.GetDictionary("data");
|
|||
|
int timestamp = dataT.GetInt("timestamp");
|
|||
|
Launch.SetServerTimestamp(timestamp);
|
|||
|
AntiCheatProxy.Instance.SetServerTimestamp(timestamp);
|
|||
|
callback?.Invoke(0, "");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
callback?.Invoke(errorCode, errorMsg);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="callback"></param>
|
|||
|
public void UpdateUserInfo(string nickName, string avatarURL)
|
|||
|
{
|
|||
|
if (!CheckLogin())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var jsonData = GetCommonData();
|
|||
|
jsonData.Add("nick", nickName);
|
|||
|
jsonData.Add("avatar", avatarURL);
|
|||
|
|
|||
|
var body = F.Utils.ToJsonBytes(jsonData);
|
|||
|
StartCoroutine(Request("/api/user/update_user", body, process));
|
|||
|
|
|||
|
void process(int error, object data)
|
|||
|
{
|
|||
|
if (error == 0)
|
|||
|
{
|
|||
|
var table = F.Utils.ToJsonTable((byte[])data);
|
|||
|
int errorCode = table.GetInt("code");
|
|||
|
if (errorCode == 0)
|
|||
|
{
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string errorMsg = table.GetString("msg");
|
|||
|
OnServerError(errorCode, errorMsg);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OnNetworkError(error, data?.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
private readonly Dictionary<string, object> _jsonData = new Dictionary<string, object>();
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
private Dictionary<string, object> GetCommonData(bool withChannel = false)
|
|||
|
{
|
|||
|
_jsonData.Clear();
|
|||
|
_jsonData.Add("uid", uid);
|
|||
|
if (withChannel)
|
|||
|
{
|
|||
|
_jsonData.Add("channel", KPlatform.Instance.channel);
|
|||
|
}
|
|||
|
return _jsonData;
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Sign && Request
|
|||
|
|
|||
|
#if TEST_SERVER
|
|||
|
public static string SERVER_URL = "https://dev-shaoxia.lanfeitech.com";
|
|||
|
#else
|
|||
|
public static string SERVER_URL = "https://prod-shaoxia.lanfeitech.com";
|
|||
|
#endif
|
|||
|
|
|||
|
IEnumerator Request(string api, byte[] body, System.Action<int, object> callback)
|
|||
|
{
|
|||
|
var url = SERVER_URL + api;
|
|||
|
|
|||
|
var webRequest = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST)
|
|||
|
{
|
|||
|
uploadHandler = new UploadHandlerRaw(body)
|
|||
|
{
|
|||
|
contentType = "application/json"
|
|||
|
},
|
|||
|
downloadHandler = new DownloadHandlerBuffer()
|
|||
|
};
|
|||
|
|
|||
|
var timeStr = Launch.Timestamp.ToString();
|
|||
|
|
|||
|
var requestSign = GetSign(body, timeStr);
|
|||
|
webRequest.SetRequestHeader("sign", requestSign);
|
|||
|
webRequest.SetRequestHeader("time", timeStr);
|
|||
|
if (!string.IsNullOrEmpty(token))
|
|||
|
{
|
|||
|
webRequest.SetRequestHeader("authorization", token);
|
|||
|
}
|
|||
|
#if UNITY_EDITOR
|
|||
|
Debug.Log("send:" + System.Text.Encoding.UTF8.GetString(body));
|
|||
|
#endif
|
|||
|
yield return webRequest.SendWebRequest();
|
|||
|
if (webRequest.isNetworkError || webRequest.isHttpError)
|
|||
|
{
|
|||
|
#if UNITY_EDITOR
|
|||
|
Debug.Log("recv_error:" + webRequest.error);
|
|||
|
#endif
|
|||
|
callback(ErrorCode.NETWORK_ERROR, webRequest.error);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
var data = webRequest.downloadHandler.data;
|
|||
|
#if UNITY_EDITOR
|
|||
|
Debug.Log("recv:" + System.Text.Encoding.UTF8.GetString(data));
|
|||
|
#endif
|
|||
|
var responseSign = webRequest.GetResponseHeader("sign");
|
|||
|
#if SDK_WECHAT_WASM
|
|||
|
var headers = webRequest.GetResponseHeaders();
|
|||
|
if ((headers.Count < 2 && string.IsNullOrEmpty(responseSign)) || responseSign == GetSign(data, timeStr))
|
|||
|
#else
|
|||
|
if (responseSign == GetSign(data, timeStr))
|
|||
|
#endif
|
|||
|
{
|
|||
|
callback(0, data);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
callback(2, "签名错误");
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#if TEST_SERVER
|
|||
|
//"appid=KHQiAUtB&body={0}&signkey=3c3c3aa984ccbbc791655fbf9f8e0fb1&time={1}&"
|
|||
|
static readonly byte[] _KeyArray = new byte[] { 97, 112, 112, 105, 100, 61, 75, 72, 81, 105, 65, 85, 116, 66, 38, 98, 111, 100, 121, 61, 123, 48, 125, 38, 115, 105, 103, 110, 107, 101, 121, 61, 51, 99, 51, 99, 51, 97, 97, 57, 56, 52, 99, 99, 98, 98, 99, 55, 57, 49, 54, 53, 53, 102, 98, 102, 57, 102, 56, 101, 48, 102, 98, 49, 38, 116, 105, 109, 101, 61, 123, 49, 125, 38 };
|
|||
|
//static readonly int _Key1Index = 20;
|
|||
|
#else
|
|||
|
//"appid=KHQiAUtB&body={0}&signkey=8adb871756f5bdea6b3d57547fdec8d9&time={1}&"
|
|||
|
readonly static byte[] _KeyArray = new byte[] { 97, 112, 112, 105, 100, 61, 75, 72, 81, 105, 65, 85, 116, 66, 38, 98, 111, 100, 121, 61, 123, 48, 125, 38, 115, 105, 103, 110, 107, 101, 121, 61, 56, 97, 100, 98, 56, 55, 49, 55, 53, 54, 102, 53, 98, 100, 101, 97, 54, 98, 51, 100, 53, 55, 53, 52, 55, 102, 100, 101, 99, 56, 100, 57, 38, 116, 105, 109, 101, 61, 123, 49, 125, 38 };
|
|||
|
#endif
|
|||
|
static string _KeyIn;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="body"></param>
|
|||
|
/// <param name="timestamp"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static string GetSign(string body, string timestamp)
|
|||
|
{
|
|||
|
if (string.IsNullOrEmpty(_KeyIn))
|
|||
|
_KeyIn = System.Text.Encoding.ASCII.GetString(_KeyArray);
|
|||
|
var raw_str = F.Utils.Text.Format(_KeyIn, body, timestamp);
|
|||
|
return F.Utils.Security.GetMd5Hex(raw_str);
|
|||
|
}
|
|||
|
|
|||
|
static System.IO.MemoryStream _SignStream;
|
|||
|
public static string GetSign(byte[] body, string timestamp)
|
|||
|
{
|
|||
|
if (_SignStream == null)
|
|||
|
_SignStream = new System.IO.MemoryStream();
|
|||
|
|
|||
|
_SignStream.SetLength(0);
|
|||
|
_SignStream.Write(_KeyArray, 0, 20);
|
|||
|
_SignStream.Write(body, 0, body.Length);
|
|||
|
_SignStream.Write(_KeyArray, 23, 47);
|
|||
|
|
|||
|
var timeBytes = System.Text.Encoding.UTF8.GetBytes(timestamp);
|
|||
|
_SignStream.Write(timeBytes, 0, timeBytes.Length);
|
|||
|
_SignStream.Write(_KeyArray, 73, 1);
|
|||
|
|
|||
|
_SignStream.Position = 0;
|
|||
|
return F.Utils.Security.GetMd5Hex(_SignStream);
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Proxy
|
|||
|
|
|||
|
public static ServerProxy Instance => GetInstance<ServerProxy>();
|
|||
|
|
|||
|
#endregion
|
|||
|
}
|
|||
|
}
|