初探基于GameProtocol和NetFrame的RPG服务器
最近海涛大神分享了一个用于Unity的游戏服务器RPGDemoServer,基于.net开发.目前开始记录学习.
文章目录
- 总体框架
- 服务器端
- 客户端
- 服务器是如何开始的
- 新建一个服务器
- 基于事件驱动
- 注册模块和登陆模块
- 新建一个客户端
- 基础模块
- NetIO
- 注册/登陆模块
- NetManager
- 开始聊天
- 聊天失败
- 服务端没有角色信息
- 新建一个角色
- 成功收到信息
总体框架
服务器端
客户端
此框架比较庞大,应用于rpg项目基本业务是足够了.
服务器通讯部分主要是上面Net包,里面有注册,登陆,背包,好友系统,副本,技能,组队等模块.
它是以线程驱动NGUI的形式进行的,新版本的unity默认是无法运行的.
但是根据我的需求,目前精简到下面就够了,有些留在后面研究:
上面有注册模块,还有聊天模块.后面也许会加入组队模块.
服务器是如何开始的
新建一个服务器
ServerStart ss = new ServerStart(9999);ss.Decode = MessageEncoding.Decode;ss.Encode = MessageEncoding.Encode;ss.LengthDecode = LengthEncoding.Decode;ss.LengthEncode = LengthEncoding.Encode;ss.HandlerCenter =new MessageHandlerCenter();ss.Start(1234);Console.WriteLine("服务启动成功");
这看起来是一个基本的服务器实例化代码
9999 是最大连接数.
1234 代表端口号.
中间有几行看起来是加密和解密,还有消息处理的地方.
基于事件驱动
public override void ClientClose(UserToken userToken, string error){Console.WriteLine("有客户端断开连接!");try{_mapHandler.ClientClose(userToken);}finally{_userHandler.ClientClose(userToken);_loginHandler.ClientClose(userToken);}}public override void ClientConnect(UserToken userToken){Console.WriteLine("有客户端连接进来了!");}
我很喜欢这种事件驱动.
public override void MessageReceive(UserToken userToken, object message){SocketModel socketModel = message as SocketModel;if(socketModel!=null)switch (socketModel.Type){case Protocol.TYPE_LOGIN:_loginHandler.MessageReceive(userToken, socketModel);break;case Protocol.TYPE_USER:_userHandler.MessageReceive(userToken, socketModel);break;case Protocol.TYPE_MAP:_mapHandler.MessageReceive(userToken,socketModel);break;case Protocol.TYPE_RAID:_raidHandler.MessageReceive(userToken,socketModel);break;case Protocol.TYPE_CHAT:_chatHandler.MessageReceive(userToken,socketModel);break;}}
我不太喜欢这种以二进制的形式来通讯,这样就无法直接通过调试工具进行查看和测试.
注册模块和登陆模块
public void MessageReceive(UserToken userToken, SocketModel message){switch (message.Command){case LoginProtocol.LOGIN_CREQ:Console.WriteLine("登陆");Login(userToken, message.GetMessage<AccountDTO>());break;case LoginProtocol.REG_CREQ:Console.WriteLine("注册");Reg(userToken, message.GetMessage<AccountDTO>());break;case LoginProtocol.OFFLINE_CREQ:ClientClose(userToken);break;}}public void ClientClose(UserToken userToken){ExecutorPool.Instance.Executor(delegate (){_accountBiz.Close(userToken);});}private void Login(UserToken userToken ,AccountDTO value){ExecutorPool.Instance.Executor(delegate(){int result = _accountBiz.AccountLogin(userToken, value.Account, value.Password);Console.WriteLine("登陆结果=" + result);Write(userToken,Protocol.TYPE_LOGIN,0,LoginProtocol.LOGIN_SRES,result);});}private void Reg(UserToken userToken ,AccountDTO value){ExecutorPool.Instance.Executor(delegate (){int result = _accountBiz.CreateAccount(userToken, value.Account, value.Password);Console.WriteLine("注册结果="+ result);Write(userToken, Protocol.TYPE_LOGIN, 0, LoginProtocol.REG_SRES, result);});}
里面是怎么判断的 ,就不进去看了,目前只要结果就行了.
新建一个客户端
基础模块
我新建了一个unity项目来尝试和服务端进行通讯测试,基本的项目如下:
global 是全局项目,用来存储一些信息.
NetManager是用来跟服务器通讯的工具.我加入了登陆/注册,聊天模块.
NetIO
主要socket代码都在里面,应该是在NetManagerUtil 的update 里被激活的.如果没有连上服务器,貌似就在假死状态,所以这里可以做下调整.
注册/登陆模块
using UnityEngine;
using System.Collections;
using GameProtocol;
using GameProtocol.Auto;public class LoginHandler : MonoBehaviour {public delegate void loginEventHandler(int ms);public event loginEventHandler LoginEvent;public delegate void RegEventHandler(int ms);public event RegEventHandler RegEvent;public void MessageReceive(SocketModel socketModel){switch (socketModel.Command){case 0:Debug.Log("登陆信息");LoginAccount(socketModel.GetMessage<int>());break;case 2:Debug.Log("注册信息");RegAccount(socketModel.GetMessage<int>());break;} }private void RegAccount(int result){switch (result){case -1:Debug.Log("账号重复!");if (RegEvent != null){RegEvent(0);}break; case 0:if (RegEvent != null){RegEvent(1);}Debug.Log("注册成功!");break; }}private void LoginAccount(int result){switch (result){case 0:Global.Instance.IsOnline = true;Debug.Log("登陆成功");if(LoginEvent!=null){LoginEvent (1);}break;case -1:if (LoginEvent != null){LoginEvent(0);}Debug.Log("账号不存在!");break; case -2:if (LoginEvent != null){LoginEvent(2);}Debug.Log("账号在线!");break; case -3:if (LoginEvent != null){LoginEvent(3);}Debug.Log("账号或密码错误!");break; case -4:if (LoginEvent != null){LoginEvent(4);}Debug.Log("账号密码为空!");break; }}
}
我给它加了注册事件还有登陆事件.
NetManager
using UnityEngine;
using System.Collections;
using GameProtocol;
using GameProtocol.Auto;public class NetManagerUtil : MonoBehaviour
{private LoginHandler _loginHandler;private ChatHandler _chatHandler;void Start(){DontDestroyOnLoad(this.gameObject);_loginHandler = this.GetComponent<LoginHandler>();_loginHandler.LoginEvent += handleLoginEvent;_loginHandler.RegEvent += handleRegEvent;_chatHandler = this.GetComponent<ChatHandler>();zhuce();}private void handleLoginEvent(int result){Debug.Log("登陆结果="+result);}private void handleRegEvent(int result){Debug.Log("注册结果=" + result);if(result==1){login();}}void zhuce(){// yield return new WaitForSeconds(2f);AccountDTO accountDto = new AccountDTO();accountDto.Account = "abc";accountDto.Password = "123";this.Write(Protocol.TYPE_LOGIN, 0, LoginProtocol.REG_CREQ, accountDto);Global.Instance.Account = "abc";Global.Instance.Password = "123";}void login(){AccountDTO accountDto = new AccountDTO();accountDto.Account = Global.Instance.Account;accountDto.Password = Global.Instance.Password;this.Write(Protocol.TYPE_LOGIN, 0, LoginProtocol.LOGIN_CREQ, accountDto);}void Update(){while (NetIO.Instance.MessageList.Count > 0){SocketModel socketModel = NetIO.Instance.MessageList[0];StartCoroutine("MessageReceive", socketModel);NetIO.Instance.MessageList.RemoveAt(0);}}private void MessageReceive(SocketModel socketModel){switch (socketModel.Type){case 0:_loginHandler.MessageReceive(socketModel);break;case 1:break;case 2:break;case 3:break;case 4:_chatHandler.MessageReceive(socketModel);break;}}
}
目前加入了自动注册和登陆,现在看来一切结果都在预期之中.
开始聊天
聊天失败
我先在登陆结果的地方直接输入聊天内容,服务器是收到了,但是客户端并没有收到
private void handleLoginEvent(int result){Debug.Log("登陆结果="+result);if (result == 1){ChatDTO dto = new ChatDTO(Global.Instance.UserDto, "世上没有一件工作不辛苦,没有一处人事不复杂。不要随意发脾气,谁都不欠你的。学会低调,取舍间必有得失,不用太计较。学着踏实而务实,越努力越幸运。当一个人有了足够的内涵和物质做后盾,人生就会变得底气十足。若是美好,叫做精彩,若是糟糕,叫做经历。——互勉", ChatType.System, null);this.Write(Protocol.TYPE_CHAT, 0, ChatProtocol.CHAT_TALK_CREQ, dto);}}
以下是聊天模块:
public void MessageReceive(SocketModel socketModel){switch (socketModel.Command){case 1:break;case 2:ChatDTO dto = socketModel.GetMessage<ChatDTO>();switch (dto.Type){case ChatType.System:SystemChatList.Add(dto);Debug.Log("System="+dto.PlayerDto.Name+":"+ dto.ChatValue);break;case ChatType.Team:string text = string.Format("[ff0000]【队伍】[-] [ffff00]{0}[-] :{1}", dto.PlayerDto.Name,dto.ChatValue);Debug.Log("Team=" + dto.PlayerDto.Name + ":" + dto.ChatValue);/* if (BottomEnumPanel.Instance != null){BottomEnumPanel.Instance.TextList.Clear();BottomEnumPanel.Instance.TextList.Add(text);}*/TeamChatList.Add(dto);// CreateChatItem(1, dto);break;case ChatType.World:string value = string.Format("[ff0000]【世界】[-] [ffff00]{0}[-] :{1}", dto.PlayerDto.Name,dto.ChatValue);/* if (BottomEnumPanel.Instance != null){BottomEnumPanel.Instance.TextList.Clear();BottomEnumPanel.Instance.TextList.Add(value);}*/WorldChatList.Add(dto);//CreateChatItem(0,dto);break; }AllChatList.Add(dto);break; }}
以上并没有被触发.
服务端没有角色信息
private void Talk(UserToken userToken , ChatDTO dto){switch (dto.Type){case ChatType.World:Console.WriteLine("世界频道");break;case ChatType.System:int[] playerIds = _userBiz.GetAllOnlinePlayerId(userToken);Console.WriteLine("系统频道");Console.WriteLine("playerIds length="+ playerIds.Length);for (int i=0;i<playerIds.Length;i++){Console.WriteLine("id="+playerIds[i]);}this.Write(playerIds, Protocol.TYPE_CHAT,0,ChatProtocol.CHAT_TALK_BRO, dto);break;case ChatType.Team:Console.WriteLine("组队频道");this.Write(dto.TeamId, Protocol.TYPE_CHAT, 0, ChatProtocol.CHAT_TALK_BRO, dto);break;}Console.WriteLine("文字内容="+dto.ChatValue);}
检查这里,原来playerIds length=0;
新建一个角色
应该是因为没有角色,所以才没有办法发给客户端.那还是加入角色模块吧,先新建一个:
private void addUser(){UserDTO userDto = new UserDTO();userDto.Name = "战士";userDto.JobId = 0;this.Write(Protocol.TYPE_USER, 0, UserProtocol.Create_CREQ, userDto);}private void handleLoginEvent(int result){Debug.Log("登陆结果="+result);if (result == 1){addUser();}}
UserHandler下改成这样:
...Debug.Log("socketModel.Command="+ socketModel.Command);switch (socketModel.Command){case 1://创建角色Debug.Log("创建角色");Create(socketModel.GetMessage<bool>());break;case 3://获取角色信息Debug.Log("获取角色信息");GetPlayerIdList(socketModel.GetMessage<List<int>>());break;case 5://获取角色数据Debug.Log("获取角色数据");_userDtos = socketModel.GetMessage<List<UserDTO>>();for(int i=0;i<_userDtos.Count;i++){UserDTO userD = _userDtos[i];Debug.Log("name="+ userD.Name);Debug.Log("id=" + userD.Id);Debug.Log("job=" + userD.JobId);}//UIMgr.Instance.Destory(PanelType.SelectPlayerPanel);// UIMgr.Instance.ShowPanel(PanelType.SelectPlayerPanel, _userDtos);break;case 13:Global.Instance.UserDto = socketModel.GetMessage<UserDTO>();if (OnPlayerInfoChange != null)OnPlayerInfoChange();break;...
运行结果:
新建角色成功.再来试试聊天吧.
成功收到信息
_userHandler.OnPlayerCreate += userCreated;private void userCreated(){Debug.Log("角色新建成功");talk();}
初探基于GameProtocol和NetFrame的RPG服务器相关推荐
- 基于VMware vSphere 5.0的服务器虚拟化实践(9)
基于VMware vSphere 5.0的服务器虚拟化实践(9) 2012-12-31 13:07:57 标签:虚拟化 VMware vSphere 十全十美 原创作品,允许转载,转载时请务必以超链接 ...
- 基于第三方开源库的OPC服务器开发指南(2)——LightOPC的编译及部署
基于第三方开源库的OPC服务器开发指南(2)--LightOPC的编译及部署 前文已经说过,OPC基于微软的DCOM技术,所以开发OPC服务器我们要做的事情就是开发一个基于DCOM的EXE文件.一个代 ...
- 基于epoll实现简单的web服务器
1. 简介 epoll 是 Linux 平台下特有的一种 I/O 复用模型实现,于 2002 年在 Linux kernel 2.5.44 中被引入.在 epoll 之前,Unix/Linux 平台下 ...
- Boost:基于boost::asio的延迟udp服务器测试程序
Boost:基于boost::asio的延迟udp服务器测试程序 实现功能 C++实现代码 客户端源码 服务端源码 实现功能 boost::asio模块,基于boost::asio的延迟udp服务器测 ...
- Boost:基于boost::asio的延迟tcp服务器测试程序
Boost:基于boost::asio的延迟tcp服务器测试程序 实现功能 C++实现代码 客户端源码 服务端源码 实现功能 boost::asio模块,基于boost::asio的延迟tcp服务器测 ...
- linux环境下企业基于域名访问的web于电子邮件服务器 论文,基于Linux平台的企业邮件服务器搭建...
我失骄杨君失柳,杨柳轻飏直上重霄九.得道多助,失道寡助.身后有余忘缩手,眼前无路想回头.鸟宿池边树,僧敲月下门.想当年,金戈铁马,气吞万里如虎. 本文由418133804贡献 pdf文档可能在WAP端 ...
- RHEL5 基于虚拟用户验证的VSFTP服务器
RHEL5基于虚拟用户验证的VSFTP服务器 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:off ...
- arm Linux 低成本方案,参赛作品《低成本基于ARM+Linux平台搭建web服务器的物联网学习板》...
[报名阶段需要填写的内容] 1. 参赛者姓名(必填项): 王徕泽 2. 单位或学校名称(选填项): 徕泽电子工作室 3. 当前职务或职称(选填项): 室长 4. 参赛作品的名字(必填项): 低成本基于 ...
- 实验:基于keepalived实现两台realserver服务器中的nginx和php-fpm服务互为主从
基于keepalived实现两台realserver服务器中的nginx和php-fpm服务互为主从 思路:利用两个VIP,一个定位nginx,一个定位php-fpm 步骤: 1.准备两台基于LNMP ...
最新文章
- caffe修改需要的东西 6:40
- 北航成AAAI 2021最大赢家,两篇一作斩获最佳论文、提名奖,研究皆与Transformer相关...
- C++中extern “C”含义深层探索(zz)
- Python3 绘制同心圆代码示例
- spark mllib和ml类里面的区别
- scp和ssh如何连接指定端口的远程主机
- linux 省内存的桌面,Linux_在Linux中可视化显示内存占用情况的方法,物理内存不足对Linux桌面系统 - phpStudy...
- 【英语学习】【WOTD】darling 释义/词源/示例
- DNS原理及其解析过程 精彩剖析
- 英文操作系统下WebBrowser控件无法显示本地页面的解决方法
- [补档]noip2019集训测试赛(十五)
- hdu1520 (树形dp)
- 数组中只出现一次的数字(python解法)
- USB设备驱动理解(wds)
- SM4算法java代码实现
- 元宇宙地产演化史:从文本时代到区块链时代
- 我们为什么要结婚?(特别有道理)
- linux 第十七天 linuxprobe
- BZOJ 4408: [Fjoi 2016]神秘数(可持久化线段树)
- VS2019 C# MySQL 学生信息增删改查(二、改查(续前节))