/******************************** * * 作者:吴隆健 * 创建时间: 2019/7/3 13:06:48 * ********************************/ using GameData; using GameDAL.Game; using MrWu.Debug; using Server.DB.Redis; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Data; using MrWu.Time; using System.Threading; using Server.Data; using System.IO; using System.Text; using System.Threading.Tasks; using Server.Core; using Server.DB.Sql; ///// ///// 玩家数据管理 ///// namespace Server { /// /// /// public class PlayerDataManager { /// /// /// public static PlayerDataManager instance { get; private set; } private PlayerDataManager() { } static PlayerDataManager() { instance = new PlayerDataManager(); } #region player redis 字段 /// /// 玩家的userid 字段 /// public const string useridField = "userid"; /// /// 用户名 /// public const string UserNameField = "UserName"; /// /// 玩家昵称 /// public const string nickNameField = "NickName"; /// /// 数据版本 /// public const string verField = "ver"; /// /// 玩家金额字段 /// public const string moneyField = "money"; /// /// 游戏币变化字段 /// public const string moneyChangeField = "moneyChange"; /// /// 金币字段名 /// public const string goldFiled = "Glod"; /// /// 金币变化字段名 /// public const string goldChangeField = "GlodChange"; /// /// 性别字段名 /// public const string sexField = "Sex"; /// /// 地区字段 /// public const string areaField = "Area"; /// /// 经验字段 /// public const string expField = "hjha_Exp"; /// /// 经验变化字段 /// public const string expChangeField = "expChange"; /// /// 玩家的奖券字段 /// public const string couponField = "coupon"; /// /// 奖券变化字段名 /// public const string couponChangeField = "couponChange"; /// /// 房卡字段名 /// public const string fangkaField = "fangka"; /// /// 房卡变化字段名 /// public const string FangKaChangeField = "fangkaChange"; /// /// 用户类型 /// public const string UserTypeField = "UserType"; /// /// 代理字段 /// public const string DailiField = "daili"; /// /// 防沉迷字段 /// public const string PiField = "pi"; /// /// 比赛卷字段 /// public const string MatchRollField = "mmnn"; /// /// 比赛卷变化字段 /// public const string MatchRollChangeField = "mmnnChange"; /// /// 推广的标识 /// public const string platTagField = "platTag"; /// /// 用户头像对应的字段 /// public const string HeadField = "hjha_LeagueId"; /// /// 平台类型 /// public const string PlatEnumField = "hjha_fy_Value"; /// /// 在redis中的玩家列表 /// public const string PlayerListKey = "Game:PlayerLRU:Players"; #endregion /// /// 获取玩家的数据的key /// /// /// public static string GetKey(int userid) { return RedisPlayerDataKeyPrefix + userid; } /// /// redis中玩家数据的key /// public const string RedisPlayerDataKeyPrefix = "Game:PlayerData:"; /// /// 头像 /// public const string AvatarFidle = "avatar"; /// /// 从redis中加载玩家数据 /// /// /// 变化量 /// public static int LoadRedisPlayerData(playerData pl, out PlayerDataChange cgdata) { var changeData = PlayerDataChange.Create(pl.userid); cgdata = changeData; //-1表示为加载到玩家 int result = -1; string key = GetKey(pl.userid); //临时变量 long tmplong; int tmpint; var fields = redisManager.db.HashGetAll(key); if (fields == null) //redis 中没有玩家数据 return result; foreach (var he in fields) { switch (he.Name) { case nickNameField: pl.nickname = he.Value; break; case UserNameField: pl.UserName = he.Value; break; case moneyField: if (he.Value.TryParse(out tmplong)) { pl.money = tmplong; } else { Debug.Error("错误错误,玩家游戏币数据竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); } break; case moneyChangeField: if (he.Value.TryParse(out tmplong)) { changeData.moneyChange = tmplong; } else { Debug.Error("错误错误,玩家的游戏币变化值竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); } break; case goldFiled: if (he.Value.TryParse(out tmpint)) { pl.gold = tmpint; } else { Debug.Error("错误错误,玩家的金币数据竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); } break; case goldChangeField: if (he.Value.TryParse(out tmpint)) { changeData.goldChange = tmpint; } else { Debug.Error("错误错误,玩家的游戏币数据竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); } break; case sexField: if (he.Value.TryParse(out tmpint)) { pl.sex = tmpint; } else { Debug.Error("错误错误,玩家的性别字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); } break; case areaField: pl.area = he.Value; break; case expField: if (he.Value.TryParse(out tmpint)) { pl.exp = tmpint; } else { Debug.Error("错误错误,玩家的经验字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); } break; case expChangeField: if (he.Value.TryParse(out tmpint)) { changeData.expChange = tmpint; } else { Debug.Error("错误错误,玩家的经验字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); } break; case couponField: if (he.Value.TryParse(out tmpint)) { pl.coupon = tmpint; } else { Debug.Error("错误错误,玩家的奖券字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); } break; case couponChangeField: if (he.Value.TryParse(out tmpint)) { changeData.couponChange = tmpint; } else { Debug.Error("错误错误,玩家的奖券变化字段竟然不是数值 userid:{0} vlaue:{1}", pl.userid, he.Value); } break; case fangkaField: if (he.Value.TryParse(out tmpint)) { pl.fangka = tmpint; } else { Debug.Error("错误错误,玩家的房卡字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); } break; case FangKaChangeField: if (he.Value.TryParse(out tmpint)) { changeData.fangkaChange = tmpint; } else { Debug.Error("错误错误,玩家的房卡变化值字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); } break; case UserTypeField: if (he.Value.TryParse(out tmpint)) { pl.userType = tmpint; } else { Debug.Error("asckjagbshdbashbsmbaxas :" + he.Value); } break; case AvatarFidle: pl.avatar = he.Value; break; case DailiField: tmpint = 0; if (he.Value.TryParse(out tmpint)) pl.daili = tmpint; else Debug.Error("错误错误,玩家代理字段竟然不是数值 userid{0} value{1}", pl.userid, he.Value); break; case PlatEnumField: tmpint = 0; if (he.Value.TryParse(out tmpint)) pl.PlatEnum = tmpint; else Debug.Error("错误错误,玩家代理字段竟然不是数值 userid{0} value{1}", pl.userid, he.Value); break; case PiField: pl.pi = he.Value; break; case platTagField: pl.platTag = he.Value; break; case HeadField: tmpint = 0; if (he.Value.TryParse(out tmpint)) { pl.HeadType = tmpint; } else { Debug.Error("错误错误,玩家的头像字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); } break; case MatchRollField: tmpint = 0; if (he.Value.TryParse(out tmpint)) { pl.MatchRoll = tmpint; } else { Debug.Error("错误错误,玩家的比赛卷字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); } break; case MatchRollChangeField: if (he.Value.TryParse(out tmpint)) { changeData.MatchRollChange = tmpint; } else { Debug.Error("错误错误,玩家的比赛卷变化值字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); } break; case "RegTime": string temp = he.Value; try { pl.RegTime = string.IsNullOrWhiteSpace(temp) ? default(DateTime) : Convert.ToDateTime(temp); } catch { pl.RegTime = default(DateTime); } break; } } PlayerFun.PlayerAddChangeData(pl, changeData); result = 1; return result; } /// /// 加载玩家数据如果 sql中的ver 与 参数 ver 不一致 更新redis 以及 pl 原始数据 并添加版本ver信息 /// //加载sql 数据 并更新 redis 数据 public static bool LoadSqlPlayerData(playerData pl, int ver, PlayerDataChange pdc) { int userid = pl.userid; string key = GetKey(pl.userid); DataSet ds = PlayerDB.ReadPlayerData(pl.userid, !string.IsNullOrEmpty(pl.pi)); if (ds.Tables == null || ds.Tables.Count < 1 || ds.Tables[0].Rows.Count <= 0) { //版本一致,数据取redis 原来的数据 return false; //redis 中的版本 与数据库中的版本一致 则有玩家, 版本不一致却没数据,表示没有这个用户 } //读取玩家数据 DataTable gameTable = ds.Tables[0]; //昵称 string db_nickname; //地区 string db_area; //头像 string db_avatar; //头像 string db_userName; //用户类型 int db_UserType; //经验值 int db_exp; //性别 int db_sex; //游戏币 long db_money; //奖券 int db_coupon = 0; //房卡 int db_fangka; //金币 int db_glod; int db_daili = 0; db_nickname = gameTable.Rows[0]["nickNameEm"] as string; if (string.IsNullOrEmpty(db_nickname)) { db_nickname = gameTable.Rows[0][nickNameField] as string; } db_userName = gameTable.Rows[0]["userName"] as string; if (db_nickname == null) db_nickname = string.Empty; db_area = gameTable.Rows[0][areaField] as string; if (db_area == null) db_area = string.Empty; db_sex = (int)gameTable.Rows[0][sexField]; db_money = (long)gameTable.Rows[0][moneyField]; db_exp = (int)gameTable.Rows[0][expField]; db_fangka = (int)gameTable.Rows[0]["xu"]; db_UserType = (int)gameTable.Rows[0][UserTypeField]; db_coupon = (int)gameTable.Rows[0][couponField]; db_avatar = gameTable.Rows[0][AvatarFidle] as string; db_daili = (int)gameTable.Rows[0][DailiField]; var platEnum = (int)gameTable.Rows[0][PlatEnumField]; string pi = pl.pi; if (string.IsNullOrEmpty(pl.pi)) { pi = (gameTable.Rows[0][PiField] as string) ?? string.Empty; } string platTag = (gameTable.Rows[0][platTagField] as string) ?? string.Empty; int matchRoll = (int)gameTable.Rows[0][MatchRollField]; int head = (int)gameTable.Rows[0][HeadField]; DateTime regTime = default(DateTime); try { regTime = (DateTime)gameTable.Rows[0]["RegTime"]; } catch { } if (db_avatar == null) db_avatar = string.Empty; db_glod = (int)gameTable.Rows[0]["hjha_Gold"]; List hes = new List(); hes.Add(new HashEntry(useridField, userid)); hes.Add(new HashEntry(UserNameField,db_userName)); hes.Add(new HashEntry(nickNameField, db_nickname)); hes.Add(new HashEntry(moneyField, db_money)); hes.Add(new HashEntry(sexField, db_sex)); hes.Add(new HashEntry(areaField, db_area)); hes.Add(new HashEntry(expField, db_exp)); hes.Add(new HashEntry(couponField, db_coupon)); hes.Add(new HashEntry(fangkaField, db_fangka)); hes.Add(new HashEntry(UserTypeField, db_UserType.ToString())); hes.Add(new HashEntry(AvatarFidle, db_avatar)); hes.Add(new HashEntry(goldFiled, db_glod)); hes.Add(new HashEntry(verField, ver)); hes.Add(new HashEntry(DailiField, db_daili)); hes.Add(new HashEntry(PiField, pi)); hes.Add(new HashEntry(platTagField, platTag)); hes.Add(new HashEntry("RegTime", regTime.ToString("yyyy-MM-dd HH:mm:ss"))); hes.Add(new HashEntry(MatchRollField, matchRoll)); hes.Add(new HashEntry(HeadField, head)); hes.Add(new HashEntry(PlatEnumField, platEnum)); //Debug.Info("设置玩家原始数据:" + userid + "," + db_money); redisManager.db.HashSetAsync(key, hes.ToArray()); pl.nickname = db_nickname; pl.UserName = db_userName; pl.money = db_money; pl.sex = db_sex; pl.area = db_area; pl.exp = db_exp; pl.coupon = db_coupon; pl.fangka = db_fangka; pl.userType = db_UserType; pl.avatar = db_avatar; pl.gold = db_glod; pl.daili = db_daili; pl.pi = pi; pl.PlatEnum = platEnum; pl.MatchRoll = matchRoll; pl.platTag = platTag; pl.RegTime = regTime; pl.HeadType = head; if (pdc != null) PlayerFun.PlayerAddChangeData(pl, pdc); return true; } /// /// 把玩家的变化数据清楚掉-和写sql一起使用, 原始数据+ 变化数据- /// /// /// 是否存在改玩家 public static bool ClearRedisDataChange(PlayerDataChange pdc) { string key = GetKey(pdc.userid); var tran = redisManager.GetDataBase().CreateTransaction(); tran.AddCondition(Condition.KeyExists(key)); if (pdc.moneyChange != 0) { tran.HashDecrementAsync(key, moneyChangeField, pdc.moneyChange); tran.HashIncrementAsync(key, moneyField, pdc.moneyChange); } if (pdc.expChange != 0) { tran.HashDecrementAsync(key, expChangeField, pdc.expChange); tran.HashIncrementAsync(key, expField, pdc.expChange); } if (pdc.fangkaChange != 0) { tran.HashDecrementAsync(key, FangKaChangeField, pdc.fangkaChange); tran.HashIncrementAsync(key, fangkaField, pdc.fangkaChange); } if (pdc.goldChange != 0) { tran.HashDecrementAsync(key, goldChangeField, pdc.goldChange); tran.HashIncrementAsync(key, goldFiled, pdc.goldChange); } if (pdc.couponChange != 0) { tran.HashDecrementAsync(key, couponChangeField, pdc.couponChange); tran.HashIncrementAsync(key, couponField, pdc.couponChange); } if (pdc.MatchRollChange != 0) { tran.HashDecrementAsync(key, MatchRollChangeField, pdc.MatchRollChange); tran.HashIncrementAsync(key, MatchRollField, pdc.MatchRollChange); } return tran.Execute(); } /// /// 变化数据写入redis中 /// /// 玩家数据变化值 /// 是否存在这个玩家 public static bool WriteToRedis(PlayerDataChange pdc) { string key = GetKey(pdc.userid); var tran = redisManager.GetDataBase().CreateTransaction(); if (pdc.moneyChange != 0) tran.HashIncrementAsync(key, moneyChangeField, pdc.moneyChange); if (pdc.expChange != 0) tran.HashIncrementAsync(key, expChangeField, pdc.expChange); if (pdc.goldChange != 0) tran.HashIncrementAsync(key, goldChangeField, pdc.goldChange); if (pdc.fangkaChange != 0) tran.HashIncrementAsync(key, FangKaChangeField, pdc.fangkaChange); if (pdc.couponChange != 0) tran.HashIncrementAsync(key, couponChangeField, pdc.couponChange); if (pdc.MatchRollChange != 0) tran.HashIncrementAsync(key, MatchRollChangeField, pdc.MatchRollChange); tran.AddCondition(Condition.KeyExists(key)); return tran.Execute(); } /// /// 加载redis 中的变化的数据 /// /// private static PlayerDataChange LoadRedisPlayerDataChange(int userid, bool isLock) { string key = GetKey(userid); //加载redis 中变化的数据 var hes = redisManager.db.HashGetAll(key); if (hes == null) return null; int tmpint; long tmplong; PlayerDataChange pdc =PlayerDataChange.Create(userid); foreach (var item in hes) { switch (item.Name) { case moneyChangeField: if (long.TryParse(item.Value, out tmplong)) { pdc.moneyChange = tmplong; } else { Debug.Error("redis 中玩家的游戏币变化值不是数值userid:{0},value:{1}", userid, item.Value); } break; case expChangeField: if (int.TryParse(item.Value, out tmpint)) { pdc.expChange = tmpint; } else { Debug.Error("redis 中玩家的经验变化值不是数值userid:{0},value:{1}", userid, item.Value); } break; case goldChangeField: if (int.TryParse(item.Value, out tmpint)) { pdc.goldChange = tmpint; } else { Debug.Error("redis 中金币变化值不是数值userid:{0},value:{1}", userid, item.Value); } break; case couponChangeField: if (int.TryParse(item.Value, out tmpint)) { pdc.couponChange = tmpint; } else { Debug.Error("redis 中奖券变化值不是数值userid:{0},value:{1}", userid, item.Value); } break; case FangKaChangeField: if (int.TryParse(item.Value, out tmpint)) { pdc.fangkaChange = tmpint; } else { Debug.Error("redis 中房卡变化值不是数值userid:{0},value:{1}", userid, item.Value); } break; case MatchRollChangeField: if (int.TryParse(item.Value, out tmpint)) { pdc.MatchRollChange = tmpint; } else { Debug.Error("redis 中比赛卷变化值不是数值userid:{0},value:{1}", userid, item.Value); } break; } } return pdc; } /// /// 删除玩家redis key /// /// public static void DeletePlayReids(int userid) { redisManager.db.KeyDelete(GetKey(userid)); } /// /// 保存至sql数据库 /// /// public static bool SaveToSql(int userid) { PlayerDataChange pdc = LoadRedisPlayerDataChange(userid, false); try { if (pdc != null && pdc.IsChanged) { //读取变化的数据 Debug.Info("保存玩家数据:" + pdc.userid + "," + pdc.moneyChange); //先清楚redis, sql未保存成功 再手动回滚 if (!ClearRedisDataChange(pdc)) { Debug.Error("数据竟然没有了,,,......玩家数据被穿透!!"); return false; } bool isOK = true; try { isOK = PlayerDB.SavePlayerData(pdc); } catch (Exception e) { Debug.Error("保存玩家数据时出现错误:" + e.ToString()); isOK = false; } //写入要志 if (!isOK) { try { //获取相反值,回滚 PlayerDataChange pdc2 = pdc.GetOppositeValue(true); ClearRedisDataChange(pdc2); } catch (Exception e) { Debug.Error($"回滚失败!错误错误!:{e.Message}\r\n" + pdc.ToString()); } } return isOK; } }finally { if (pdc != null) { ReferencePool.Recycle(pdc); } } return true; } public Task GetUserAccountInfoAsync(int userid) { return Task.Run(() => GetUserAccountInfo(userid)); } public UserAccountInfo GetUserAccountInfo(int userid) { var dt =GameDB.Instance.selectDb(dbbase.gamedb,"tbUserBaseInfo",new string[]{"UserName","NickName","nickNameEm"},$"userid={userid}"); if (dt.Rows.Count == 1) { var row = dt.Rows[0]; UserAccountInfo info = new UserAccountInfo(); info.UserId = userid; var userName = Convert.ToString(row["UserName"]); var nickName = Convert.ToString(row["NickName"]); var nickNameEm = Convert.ToString(row["nickNameEm"]); info.UserName = userName; info.NickName = string.IsNullOrEmpty(nickNameEm) ? nickName : nickNameEm; return info; } else { if (dt.Rows.Count < 1) { Debug.Error($"GetUserAccountInfo 未找到用户 {userid}"); } else { Debug.Error($"GetUserAccountInfo 多条数据 userid={userid}"); } return null; } } } /// /// 用户信息 /// public class UserAccountInfo { /// /// UserId /// public int UserId; /// /// 用户名 /// public string UserName; /// /// 昵称 /// public string NickName; } }