6 major server modules (PdkFriendServer/GlobalSever/ServerCore/GameModule/GameNetModule) + game logic (GameFix/GameDAL/ServerData) + network layer (NetWorkMessage) + data layer (ObjectModel) + utilities (MrWu/Core/Config/CloudAPI/dll) + adapters (zyxAdapter/base) .NET 8.0 C# solution, 16 projects, 958 source files
443 lines
14 KiB
C#
443 lines
14 KiB
C#
/********************************
|
|
*
|
|
* 作者:吴隆健
|
|
* 创建时间: 2019/7/5 11:23:27
|
|
*
|
|
********************************/
|
|
|
|
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using GameData;
|
|
using Server.Core;
|
|
using Server.Data.Module;
|
|
using Server.DB.Redis;
|
|
using Server.MQ;
|
|
using Server.Pack;
|
|
using StackExchange.Redis;
|
|
using Debug = MrWu.Debug.Debug;
|
|
|
|
namespace Server.Data
|
|
{
|
|
/// <summary>
|
|
/// 玩家锁定信息
|
|
/// </summary>
|
|
public class PlayerLockInfo
|
|
{
|
|
/// <summary>
|
|
/// 被锁的模块信息
|
|
/// </summary>
|
|
public ModuleUtile util;
|
|
|
|
/// <summary>
|
|
/// 玩家的serid
|
|
/// </summary>
|
|
public int gameid;
|
|
|
|
/// <summary>
|
|
/// 玩家的客户端id
|
|
/// </summary>
|
|
public int clientid;
|
|
|
|
/// <summary>
|
|
/// 被锁的在的游戏名称
|
|
/// </summary>
|
|
public string name;
|
|
|
|
/// <summary>
|
|
/// 是否是朋友房
|
|
/// </summary>
|
|
public bool isFriend;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 玩家操作
|
|
/// </summary>
|
|
public class PlayerFun
|
|
{
|
|
/*
|
|
* 玩家数据 在登录游戏/仓库是 加锁 在退出游戏/仓库 时解锁
|
|
* 中途可能出现宕, 在玩家登录其他游戏时,检查锁定信息,若锁定的游戏确认宕机则解锁玩家
|
|
*
|
|
* */
|
|
|
|
/// <summary>
|
|
/// 被锁的玩家列表
|
|
/// </summary>
|
|
private const string LockKey = "Game:LockPlayer:";
|
|
|
|
/// <summary>
|
|
/// 获取玩家锁定的key
|
|
/// </summary>
|
|
/// <param name="userid"></param>
|
|
/// <returns></returns>
|
|
public static string GetKey(int userid)
|
|
{
|
|
return LockKey + userid;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 锁定玩家
|
|
/// </summary>
|
|
/// <param name="userid"></param>
|
|
/// <param name="util"></param>
|
|
/// <param name="gameid"></param>
|
|
/// <param name="clientid"></param>
|
|
/// <param name="name"></param>
|
|
/// <param name="isFriend"></param>
|
|
/// <returns></returns>
|
|
public static bool LockPlayer(int userid, ModuleUtile util, int gameid, int clientid, string name,
|
|
bool isFriend)
|
|
{
|
|
string key = GetKey(userid);
|
|
PlayerLockInfo lockInfo = new PlayerLockInfo()
|
|
{
|
|
util = util,
|
|
gameid = gameid,
|
|
clientid = clientid,
|
|
name = name,
|
|
isFriend = isFriend
|
|
};
|
|
|
|
return redisManager.db.StringSet(key, JsonPack.GetJson(lockInfo), null, When.NotExists);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 修改锁信息
|
|
/// </summary>
|
|
/// <param name="userid"></param>
|
|
/// <param name="lockInfo"></param>
|
|
/// <returns></returns>
|
|
public static bool EditorLockPlayer(int userid, PlayerLockInfo lockInfo)
|
|
{
|
|
string key = GetKey(userid);
|
|
return redisManager.db.StringSet(key, JsonPack.GetJson(lockInfo), null, When.Exists);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取玩家锁定信息
|
|
/// </summary>
|
|
/// <param name="userid"></param>
|
|
/// <returns></returns>
|
|
public static Task<PlayerLockInfo> GetPlayerLockInfoAsync(int userid)
|
|
{
|
|
return Task.Run(() => GetPlayerLockInfo(userid));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取玩家锁定信息
|
|
/// </summary>
|
|
/// <param name="userid"></param>
|
|
/// <returns></returns>
|
|
public static PlayerLockInfo GetPlayerLockInfo(int userid)
|
|
{
|
|
string key = GetKey(userid);
|
|
string value = redisManager.db.StringGet(key);
|
|
|
|
return GetPlayerLockInfo(value);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="value"></param>
|
|
/// <returns></returns>
|
|
private static PlayerLockInfo GetPlayerLockInfo(string value)
|
|
{
|
|
if (string.IsNullOrEmpty(value))
|
|
return null;
|
|
return JsonPack.GetData<PlayerLockInfo>(value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解锁玩家 当玩家是朋友房加锁的 只能有本模块去解锁,不然要使用 force
|
|
/// </summary>
|
|
/// <param name="userid">解锁的玩家userid</param>
|
|
/// <param name="myUtil"></param>
|
|
/// <param name="force"></param>
|
|
/// <returns></returns>
|
|
public static bool UnLockPlayer(int userid, ModuleUtile myUtil, bool force = false)
|
|
{
|
|
#if DEBUG
|
|
Debug.Info($"UnLockPlayer {userid}");
|
|
#endif
|
|
|
|
string key = GetKey(userid);
|
|
string value = redisManager.db.StringGet(key);
|
|
|
|
var lockInfo = GetPlayerLockInfo(value);
|
|
|
|
if (lockInfo == null)
|
|
return true; //没被锁
|
|
|
|
var db = redisManager.GetDataBase();
|
|
|
|
bool r = !lockInfo.isFriend || force;
|
|
|
|
if (!r)
|
|
{
|
|
//只能本模块可以解锁
|
|
r = myUtil.Equals(lockInfo.util);
|
|
}
|
|
|
|
if (r)
|
|
{
|
|
//解锁玩家
|
|
var tran = db.CreateTransaction();
|
|
tran.AddCondition(Condition.StringEqual(key, value));
|
|
tran.KeyDeleteAsync(key);
|
|
r = tran.Execute();
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 异步获取玩家数据
|
|
/// </summary>
|
|
/// <param name="userid"></param>
|
|
/// <returns></returns>
|
|
public static Task<playerData> GetPlayerDataAsync(int userid)
|
|
{
|
|
return Task<playerData>.Factory.StartNew(
|
|
() => GetPlayerData(userid)
|
|
);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 写玩家数据
|
|
/// </summary>
|
|
/// <param name="pdc"></param>
|
|
//public static async Task WritePlayerData(Game.Data.PlayerDataChange pdc) {
|
|
public static void WritePlayerDataOnDataCenter(GameData.PlayerDataChange pdc)
|
|
{
|
|
PackHead head = PackHead.Create();
|
|
head.userid = pdc.userid;
|
|
head.packlx = ServerPackAgreement.WritePlayerData;
|
|
GlobalMQ.instance.DeliveryEveryOne((int)ModuleType.DataCenter, GlobalMQ.CreateMessage(head, pdc), true,
|
|
true);
|
|
head.Dispose();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 保存玩家数据
|
|
/// </summary>
|
|
/// <param name="userid"></param>
|
|
/// <returns></returns>
|
|
public static void SavePlayerDataOnDataCenter(int userid)
|
|
{
|
|
Thread thread = new Thread(
|
|
() =>
|
|
{
|
|
//保存玩家数据
|
|
PackHead head = PackHead.Create();
|
|
head.userid = userid;
|
|
head.packlx = ServerPackAgreement.SavePlayerData;
|
|
|
|
GlobalMQ.instance.Call_EveryOne((int)ModuleType.DataCenter, head, null);
|
|
head.Dispose();
|
|
}
|
|
);
|
|
thread.Start();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 把变化的数据添加到玩家数据上
|
|
/// </summary>
|
|
/// <param name="pl"></param>
|
|
/// <param name="pdc"></param>
|
|
public static void PlayerAddChangeData(playerData pl, GameData.PlayerDataChange pdc)
|
|
{
|
|
pl.money += pdc.moneyChange;
|
|
pl.coupon += pdc.couponChange;
|
|
pl.exp += pdc.expChange;
|
|
pl.fangka += pdc.fangkaChange;
|
|
pl.gold += pdc.goldChange;
|
|
pl.MatchRoll += pdc.MatchRollChange;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取玩家数据
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static playerData GetPlayerData(int userid)
|
|
{
|
|
//第一次读取
|
|
var result = ReadPlayerData(userid);
|
|
if (!result.Item1)
|
|
{
|
|
//不成功,尝试第二次读取
|
|
result = ReadPlayerData(userid);
|
|
if (result.Item1)
|
|
{
|
|
//成功
|
|
result.Item2.isUpper = 1;
|
|
}
|
|
else
|
|
{
|
|
//还是不成功,发到 DataCenter 请求读取
|
|
Debug.Warning($"PlayerFun GetPlayerData {userid} 第二次读取还未成功!");
|
|
//return GetPlayerDataOnDataCenter(userid);
|
|
return result.Item2;
|
|
}
|
|
}
|
|
|
|
return result.Item2;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="userid"></param>
|
|
/// <returns></returns>
|
|
public static Tuple<bool, playerData> ReadPlayerData(int userid)
|
|
{
|
|
playerData pl = new playerData();
|
|
try
|
|
{
|
|
//这是RPC方式
|
|
//3种情况
|
|
//1.redis 有 且与 sql版本一致 直接返回 redis中的数据
|
|
//2.redis 有 而与 sql版本不一致 sql数据+redis增量返回 -> 异步更新 redis 原始数据+版本
|
|
//3.redis 无 直接取 sql数据 -> 异步更新 redis原始数据 + 版本
|
|
pl.userid = userid;
|
|
|
|
//拿到redis 数据 ,没拿到 ver 就是-1 拿到则有信息
|
|
GameData.PlayerDataChange pdc;
|
|
int ver = PlayerDataManager.LoadRedisPlayerData(pl, out pdc);
|
|
|
|
//如果 ver 与数据库版本不一致 则会从新加载 并更新redis
|
|
if (!PlayerDataManager.LoadSqlPlayerData(pl, ver, pdc))
|
|
{
|
|
Debug.Warning("加载玩家失败!" + ver + ",没有这个玩家:" + pl.userid);
|
|
pl = null;
|
|
}
|
|
ReferencePool.Recycle(pdc);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.Error("读取玩家数据时出错!" + userid + "," + e.ToString());
|
|
pl = null;
|
|
}
|
|
|
|
return new Tuple<bool, playerData>(true, pl);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 保存玩家数据
|
|
/// </summary>
|
|
/// <param name="userid"></param>
|
|
/// <returns></returns>
|
|
public static Thread SavePlayerData(int userid)
|
|
{
|
|
Thread thread = new Thread(
|
|
() =>
|
|
{
|
|
var succ = PlayerDataManager.SaveToSql(userid);
|
|
if (!succ)
|
|
{
|
|
Debug.Warning($"PlayerFun SavePlayerData {userid} 第二次读取还未成功!");
|
|
//发送到 DataCenter 请求保存操作
|
|
SavePlayerDataOnDataCenter(userid);
|
|
}
|
|
}
|
|
);
|
|
thread.Start();
|
|
return thread;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 写入玩家数据
|
|
/// </summary>
|
|
/// <param name="pdc"></param>
|
|
/// <returns></returns>
|
|
public static bool WritePlayerDataInternal(GameData.PlayerDataChange pdc)
|
|
{
|
|
bool isOk = true;
|
|
Debug.ImportantLog("写库:" + pdc.ToString());
|
|
|
|
//写入成功了
|
|
if (PlayerDataManager.WriteToRedis(pdc))
|
|
{
|
|
//LastWriteList.Add(pdc.userid);
|
|
Debug.ImportantLog("写库完成!");
|
|
}
|
|
else
|
|
{
|
|
//写入失败,先冲sql中加载数据 //再写
|
|
playerData pl = new playerData()
|
|
{
|
|
userid = pdc.userid
|
|
};
|
|
|
|
if (!PlayerDataManager.LoadSqlPlayerData(pl, -1, null))
|
|
{
|
|
Debug.Error("没有这个玩家,无法写库:" + pl.userid);
|
|
return isOk;
|
|
}
|
|
else
|
|
{
|
|
if (!PlayerDataManager.WriteToRedis(pdc))
|
|
{
|
|
Debug.Fatal($"还是写库失败!!! 把包退回去! {pdc.ToString()}");
|
|
isOk = false;
|
|
//if (msg != null)
|
|
// msg.NAck(false, true);
|
|
}
|
|
else
|
|
{
|
|
//LastWriteList.Add(pdc.userid);
|
|
}
|
|
}
|
|
}
|
|
|
|
return isOk;
|
|
}
|
|
|
|
private static void WritePlayerData(PlayerDataChange pdc)
|
|
{
|
|
if (pdc.userid < 0) return;
|
|
bool isOk = false;
|
|
try
|
|
{
|
|
isOk = WritePlayerDataInternal(pdc);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.Error("WritePlayerData: " + ex);
|
|
}
|
|
|
|
if (!isOk)
|
|
{
|
|
Debug.Warning($"PlayerFun WritePlayerData {pdc.userid} 未成功!");
|
|
try
|
|
{
|
|
//发送到 DataCenter 请求写入操作
|
|
WritePlayerDataOnDataCenter(pdc);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.Error("WritePlayerDataOnDataCenter: " + ex);
|
|
}
|
|
}
|
|
|
|
ReferencePool.Recycle(pdc);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="pdc"></param>
|
|
public static Task WritePlayerDataAsync(PlayerDataChange pdc)
|
|
{
|
|
if (pdc.userid > 0)
|
|
return Task.Run(() => WritePlayerData(pdc));
|
|
else
|
|
{
|
|
ReferencePool.Recycle(pdc);
|
|
return Task.CompletedTask;
|
|
}
|
|
}
|
|
}
|
|
} |