在正式的商业游戏中,一个玩家的存档涉及很多个数据库,这里的存档使用单库,一般独立游戏使用。
存档实体类
在MySqlData文件夹内新建GlobalSave
文件
1 2 3 4 5 6 7 8 9 10 11 12 13
| using SqlSugar;
namespace MySql.MySQLData { [SugarTable("globalsave")] public class GlobalSave { [SugarColumn(IsPrimaryKey = true,IsIdentity = true)] public int Id { get; set; } public int UserId { get;set; } public string? Data { get; set; } } }
|
打开Navicat,添加globalsave表
列名 |
类型 |
长度 |
小数点 |
不是null |
主键 |
其他 |
Id |
int |
11 |
0 |
√ |
√ |
自动递增、无符号、无重复 |
UserId |
int |
11 |
0 |
√ |
|
无重复 |
Data |
MediumText |
N/A |
N/A |
|
|
|
服务器端存档读档逻辑
修改UserManager
,在其中添加存档读档的逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
|
public BaseResult GlobalSave(int id, string data) { try { GlobalSave? global = RedisMgr.Instance.GetClass<GlobalSave>(id); if (global != null) { global.Data = data; RedisMgr.Instance.SetClass(id, global); } else { global = new GlobalSave { UserId = id, Data = data }; RedisMgr.Instance.SetClass(id, global); } return BaseResult.Success; } catch (Exception ex) { Debug.LogError("用户存档失败:" + ex); return BaseResult.Failure; } }
public GetDataResult GetGlobalData(int id,out string data) { data = ""; try { GlobalSave? global = RedisMgr.Instance.GetClass<GlobalSave>(id); if (global != null) { if (global.Data != null) { data = global.Data; return GetDataResult.Success; } else { return GetDataResult.NoData; }
} else { return GetDataResult.NoData; } } catch (Exception ex) { Debug.LogError("获取用户存档失败:" + ex); return GetDataResult.Failure; } }
|
修改ServerEnum
,在其中添加获取数据的结果类型的枚举,在客户端中也要添加
1 2 3 4 5 6
| public enum GetDataResult { Success, Failure, NoData }
|
存档读档协议
这里我们准备两条协议,一条存档协议,一条读档协议
在ProtocolEnum
中添加协议,客户端也添加
1 2 3 4 5 6 7 8 9
| public enum ProtocolEnum {
MsgGlobalSave = 400, MsgGetGlobal = 401, }
|
在UserMsg
中实现协议,客户端也添加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| [ProtoContract()] public class MsgGlobalSave : MsgBase { public MsgGlobalSave() { ProtoType = ProtocolEnum.MsgGlobalSave; } [ProtoMember(1)] public override ProtocolEnum ProtoType { get; set; } [ProtoMember(2)] public string? Data { get; set; } [ProtoMember(3)] public BaseResult Result { get; set; } } [ProtoContract()] public class MsgGetGlobal : MsgBase { public MsgGetGlobal() { ProtoType = ProtocolEnum.MsgGetGlobal; } [ProtoMember(1)] public override ProtocolEnum ProtoType { get; set; } [ProtoMember(2)] public string? Data { get; set; } [ProtoMember(3)] public GetDataResult Result { get; set; } }
|
这里的Data是string类型,可以根据需要传输byte类型。客户端将存档数据转换成byte数组,服务端将byte数组转换成字符串再存进数据库。
服务器分发存读档协议
修改MsgHandler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
public static void MsgGlobalSave(ClientSocket client, MsgBase msg) { if (msg == null) { Debug.LogError(client.Socket?.RemoteEndPoint?.ToString() + "MsgGlobalSave协议接收错误!"); } else { MsgGlobalSave globalSave = (MsgGlobalSave)msg; if (globalSave.Data != null) globalSave.Result = UserManager.Instance.GlobalSave(client.UserId, globalSave.Data); else globalSave.Result = BaseResult.Failure; globalSave.Data = null; ServerSocket.Send(client, globalSave); } }
public static void MsgGetGlobal(ClientSocket client, MsgBase msg) { if(msg == null) { Debug.LogError(client.Socket?.RemoteEndPoint?.ToString() + "MsgGetGlobal协议接收错误!"); } else { MsgGetGlobal msgGetGlobal = (MsgGetGlobal)msg; msgGetGlobal.Result = UserManager.Instance.GetGlobalData(client.UserId, out string data); msgGetGlobal.Data = data; ServerSocket.Send(client,msgGetGlobal); } }
|
客户端存档读档
修改客户端的ProtocolManager
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public static void SaveDataRequest(string data,System.Action<BaseResult> callback) { MsgGlobalSave msg = new MsgGlobalSave(); NetManager.Instance.AddProtoListener(ProtocolEnum.MsgGlobalSave, (msg) => { MsgGlobalSave msgGlobalSave = msg as MsgGlobalSave; callback?.Invoke(msgGlobalSave.Result); }); msg.Data = data; NetManager.Instance.SendMessage(msg); } public static void GetDataRequest(System.Action<GetDataResult,string> callback) { MsgGetGlobal msg = new MsgGetGlobal(); NetManager.Instance.AddProtoListener(ProtocolEnum.MsgGetGlobal, (msg) => { MsgGetGlobal msgGetGlobal = msg as MsgGetGlobal; callback?.Invoke(msgGetGlobal.Result, msgGetGlobal.Data); }); NetManager.Instance.SendMessage(msg); }
|
修改NetTest
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| void Update() { if(Input.GetKeyDown(KeyCode.E)) { ProtocolManager.SaveDataRequest("SaveTest", (res) => { switch(res) { case BaseResult.Failure: Debug.Log("Save Failed"); break; case BaseResult.Success: Debug.Log("Save Success"); break; } }); } if(Input.GetKeyDown(KeyCode.F)) { ProtocolManager.GetDataRequest((res, data) => { switch (res) { case GetDataResult.Success: Debug.Log("GetSave Success: " + data); break; case GetDataResult.Failure: Debug.Log("GetSave Failure"); break; case GetDataResult.NoData: Debug.Log("GetSave NoData"); break; } }); } }
|
测试时先登录,再按下相应的按键进行测试。

Redis定时存储
现在Redis有了玩家存档数据,需要完善定时存储的逻辑了。
修改服务端UserManager
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public class UserManager : SingletonPattern<UserManager> { public void Init() { RedisMgr.Instance.SaveRedisAction += SaveDataToMysql; } private void SaveDataToMysql() { try { List<User> allUser = MySqlMgr.Instance.SqlSugarDB!.Queryable<User>().ToList(); List<GlobalSave> updateList = new List<GlobalSave>(); foreach (User user in allUser) { GlobalSave? global = RedisMgr.Instance.GetClass<GlobalSave>(user.Id); if(global != null) { updateList.Add(global); } } MySqlMgr.Instance.SqlSugarDB.Updateable(updateList).ExecuteCommand(); } catch (Exception ex) { Debug.LogError("Redis写入MySql出错:" + ex); }
} }
|