Files
hjha-server/GlobalSever/Data/PlayerDataManager.cs
xiaoou e9616125ce feat: initial commit - HJHA game server full source
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
2026-07-07 12:02:15 +08:00

880 lines
31 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/********************************
*
* 作者:吴隆健
* 创建时间: 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;
///// <summary>
///// 玩家数据管理
///// </summary>
namespace Server
{
/// <summary>
///
/// </summary>
public class PlayerDataManager
{
/// <summary>
///
/// </summary>
public static PlayerDataManager instance { get; private set; }
private PlayerDataManager()
{
}
static PlayerDataManager()
{
instance = new PlayerDataManager();
}
#region player redis
/// <summary>
/// 玩家的userid 字段
/// </summary>
public const string useridField = "userid";
/// <summary>
/// 用户名
/// </summary>
public const string UserNameField = "UserName";
/// <summary>
/// 玩家昵称
/// </summary>
public const string nickNameField = "NickName";
/// <summary>
/// 数据版本
/// </summary>
public const string verField = "ver";
/// <summary>
/// 玩家金额字段
/// </summary>
public const string moneyField = "money";
/// <summary>
/// 游戏币变化字段
/// </summary>
public const string moneyChangeField = "moneyChange";
/// <summary>
/// 金币字段名
/// </summary>
public const string goldFiled = "Glod";
/// <summary>
/// 金币变化字段名
/// </summary>
public const string goldChangeField = "GlodChange";
/// <summary>
/// 性别字段名
/// </summary>
public const string sexField = "Sex";
/// <summary>
/// 地区字段
/// </summary>
public const string areaField = "Area";
/// <summary>
/// 经验字段
/// </summary>
public const string expField = "hjha_Exp";
/// <summary>
/// 经验变化字段
/// </summary>
public const string expChangeField = "expChange";
/// <summary>
/// 玩家的奖券字段
/// </summary>
public const string couponField = "coupon";
/// <summary>
/// 奖券变化字段名
/// </summary>
public const string couponChangeField = "couponChange";
/// <summary>
/// 房卡字段名
/// </summary>
public const string fangkaField = "fangka";
/// <summary>
/// 房卡变化字段名
/// </summary>
public const string FangKaChangeField = "fangkaChange";
/// <summary>
/// 用户类型
/// </summary>
public const string UserTypeField = "UserType";
/// <summary>
/// 代理字段
/// </summary>
public const string DailiField = "daili";
/// <summary>
/// 防沉迷字段
/// </summary>
public const string PiField = "pi";
/// <summary>
/// 比赛卷字段
/// </summary>
public const string MatchRollField = "mmnn";
/// <summary>
/// 比赛卷变化字段
/// </summary>
public const string MatchRollChangeField = "mmnnChange";
/// <summary>
/// 推广的标识
/// </summary>
public const string platTagField = "platTag";
/// <summary>
/// 用户头像对应的字段
/// </summary>
public const string HeadField = "hjha_LeagueId";
/// <summary>
/// 平台类型
/// </summary>
public const string PlatEnumField = "hjha_fy_Value";
/// <summary>
/// 在redis中的玩家列表
/// </summary>
public const string PlayerListKey = "Game:PlayerLRU:Players";
#endregion
/// <summary>
/// 获取玩家的数据的key
/// </summary>
/// <param name="userid"></param>
/// <returns></returns>
public static string GetKey(int userid)
{
return RedisPlayerDataKeyPrefix + userid;
}
/// <summary>
/// redis中玩家数据的key
/// </summary>
public const string RedisPlayerDataKeyPrefix = "Game:PlayerData:";
/// <summary>
/// 头像
/// </summary>
public const string AvatarFidle = "avatar";
/// <summary>
/// 从redis中加载玩家数据
/// </summary>
/// <param name="pl"></param>
/// <param name="cgdata">变化量</param>
/// <returns></returns>
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;
}
/// <summary>
/// 加载玩家数据如果 sql中的ver 与 参数 ver 不一致 更新redis 以及 pl 原始数据 并添加版本ver信息
/// </summary>
//加载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<HashEntry> hes = new List<HashEntry>();
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;
}
/// <summary>
/// 把玩家的变化数据清楚掉-和写sql一起使用, 原始数据+ 变化数据-
/// </summary>
/// <param name="pdc"></param>
/// <returns>是否存在改玩家</returns>
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();
}
/// <summary>
/// 变化数据写入redis中
/// </summary>
/// <param name="pdc">玩家数据变化值</param>
/// <returns>是否存在这个玩家</returns>
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();
}
/// <summary>
/// 加载redis 中的变化的数据
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// 删除玩家redis key
/// </summary>
/// <param name="userid"></param>
public static void DeletePlayReids(int userid)
{
redisManager.db.KeyDelete(GetKey(userid));
}
/// <summary>
/// 保存至sql数据库
/// </summary>
/// <returns></returns>
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<UserAccountInfo> 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;
}
}
}
/// <summary>
/// 用户信息
/// </summary>
public class UserAccountInfo
{
/// <summary>
/// UserId
/// </summary>
public int UserId;
/// <summary>
/// 用户名
/// </summary>
public string UserName;
/// <summary>
/// 昵称
/// </summary>
public string NickName;
}
}