服务器Chat通信协定

修改服务器的GameMsg,添加聊天需要的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
#region 聊天相关
[System.Serializable]
public class SndChat
{
public string chat;//服务器通过session可以知道是哪个客户端的消息
}
[System.Serializable]
public class PshChat//世界聊天需要全服广播,使用push来命名
{
public string name;
public string chat;
}
#endregion

服务器ChatSys

首先给CacheSvc添加获取所有在线客户端的方法:

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
using PEProtocol;
using System.Collections.Generic;

public class ChatSys:SingletonPattern<ChatSys>
{
private CacheSvc _cacheSvc;
public void Init()
{
_cacheSvc = CacheSvc.Instance;
PECommon.Log("StrongSys Init Done");
}

public void SendChat(MsgPack msgPack)
{
SndChat data = msgPack.msg.sndChat;
PlayerData pd = _cacheSvc.GetPalyerDataBySession(msgPack.session);

GameMsg msg = new GameMsg
{
cmd = (int)CMD.PshChat,

pshChat = new PshChat
{
name = pd.name,
chat = data.chat,
}
};
//广播所有在线客户端
List<ServerSession> serverSessions = _cacheSvc.GetOnlineServerSessions();
byte[] bytes = PENet.PETool.PackNetMsg(msg);
serverSessions.ForEach(session =>
{
session.SendMsg(bytes);//使用bytes
});
}
}

这里使用session.SendMsg(bytes)而不是session.SendMsg(msg)

session.SendMsg(msg)每个session发送一次消息都要进行一次序列化,假设服务器有3000个客户端,则需要3000次序列化,但是每个客户端的世界广播消息都是一样的

session.SendMsg(bytes)将相同的广播消息提前序列化,再发送,节省CPU。使用PENet.PETool.PackNetMsg(msg)来序列化。

BuyWnd窗口复用

在游戏内点击购买体力和金币的按钮,都会弹出一样的确认窗口,但是会显示不同的确认文字,我们使用一个int值来确定每次RefreshUI时要显示的内容

BuyWnd

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
using UnityEngine;
using UnityEngine.UI;
using TMPro;

namespace DarknessWarGodLearning
{
public class BuyWnd : WindowRoot
{
[SerializeField] private Button btnClose, btnSure;
[SerializeField] private TextMeshProUGUI txtInfo;
/// <summary>
/// 0:购买体力。1:购买金币
/// </summary>
private int buyType;
protected override void InitWnd()
{
btnClose.onClick.AddListener(ClickCloseBtn);
btnSure.onClick.AddListener(ClickSureBtn);
base.InitWnd();
RefreshUI();
}
//交给MainCitySys去调用,MainCitySys再交给MainCityWnd去调用
public void SetBuyType(int buyType)
{
this.buyType = buyType;
}
private void RefreshUI()
{
switch(buyType)
{
case 0:
//体力
SetText(txtInfo, "是否花费" + Constants.Color("10钻石",TxtColor.Red) + "购买" + Constants.Color("100体力",TxtColor.Green)+"?");
break;
case 1:
//金币
SetText(txtInfo, "是否花费" + Constants.Color("10钻石",TxtColor.Red) + "购买" + Constants.Color("1000金币",TxtColor.Green)+"?");
break;
}
}
private void ClickCloseBtn()
{
audioSvc.PlayUIAudio(Constants.UIClickBtn);
SetWndState(false);
}
private void ClickSureBtn()
{
audioSvc.PlayUIAudio(Constants.UIClickBtn);
//Send Msg
}
private void OnDisable()
{
btnClose.onClick.RemoveAllListeners();
btnSure.onClick.RemoveAllListeners();
}
}
}

服务器Buy通信协定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#region 资源交易相关
[System.Serializable]
public class ReqBuy
{
public int buytype;
public int cost;
}
[System.Serializable]
public class RspBuy
{
public int buytype;
public int diamond;
public int coin;
public int power;
}
#endregion

防止客户端重复购买

如果客户端购买时服务器没有及时回应,可能会导致购买窗口无法及时关闭,玩家可能会重复点击购买。我们需要在客户端进行一些保护

BuyWnd

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
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using PEProtocol;

namespace DarknessWarGodLearning
{
public class BuyWnd : WindowRoot
{
[SerializeField] private Button btnClose, btnSure;
//...
protected override void InitWnd()
{
base.InitWnd();
//...
btnSure.interactable = true;//+
RefreshUI();
}
//...
private void ClickSureBtn()
{
//...
btnSure.interactable = false;//+
}
private void OnDisable()
{
btnClose.onClick.RemoveAllListeners();
btnSure.onClick.RemoveAllListeners();
}
}
}