项目作者: sniper00

项目描述 :
moon game server的一个使用示例,同时演示如何在服务端使用Entity Component System
高级语言: Lua
项目地址: git://github.com/sniper00/BallGame.git
创建时间: 2018-06-09T07:47:29Z
项目社区:https://github.com/sniper00/BallGame

开源协议:

关键词:
cpp ecs lua network unity

下载


项目简介

多人简易版球球大作战,游戏服务器框架moon的一个使用示例。
主要演示

  • 游戏服管理大量客户端网络连接
  • One Player One Service模式,处理玩家非共享状态,提高单服承载能力和更加Clean的内存管理
  • 异步Redis连接池服务的使用
  • 使用sharetable管理和更新游戏配表
  • 客户端消息自动注册和处理
  • 服务间消息自动注册和通信
  • 游戏逻辑编写规范
  • 服务器集群搭建
  • 服务器简易管理后台
  • 代码热更
  • 代码注解
  • 使用vscode LuaPanda 插件调试服务器代码
  • 使用vscode lua-language-server 插件提供lua代码智能能提示:
    vscode打开server目录,安装lua-language-server插件,即可获得代码提示能力,如果没有代码提示,在.vscode目录下, 创建或者修改settings.json, 添加
    1. {
    2. "Lua.workspace.library": [
    3. "./moon/lualib/",
    4. "./moon/service/"
    5. ]
    6. }



Server Message Processing

服务器消息处理



Server Message Processing

登陆界面,可选Tcp或者Websocket



Server Message Processing

游戏画面

Server结构介绍

Hub Server:

  1. 提供服务后台管理, 服务器节点配置管理(支持动态开启新服)
  2. 提供http server 和 telnet两种协议接口

Game Server开启了7种服务:

  • node 处理hub的请求消息,常用于对接SDK和服务器后台管理
  • cluster 服务器集群通信节点服务
  • gate 负责管理玩家网络连接,并转发玩家网络消息到对应玩家服务
  • auth 负责登录,创建、删除、离线加载、玩家服务
  • center 负责玩家匹配逻辑,动态创建room服务
  • user 玩家服务,一个服务对应一个玩家,处理玩家消息,管理玩家私有状态。 与其它玩家交互的消息转发到room服务。
  • room 游戏场景服务,简易球球大作战玩法逻辑

目录结构

  1. .
  2. ├── common/ #逻辑公共模块目录
  3. ├── CmdCode.lua #自动生成的和客户端通信的协议定义文件
  4. ├── Database.lua #数据库操作
  5. ├── ErrorCode.lua #逻辑错误码定义
  6. ├── GameCfg.lua #游戏配表
  7. ├── GameDef.lua #游戏内部相关配置
  8. ├── LuaPanda.lua #LuaPanda调试库
  9. ├── init.lua #common目录初始化脚本
  10. ├── protocol.lua
  11. ├── protocol_pb.lua #protobuf消息编码和解码逻辑
  12. ├── setup.lua #All In One包装: 消息注册,服务内模块注册,配表更新,代码热更
  13. ├── vector2.lua
  14. └── verify_proto.lua #开发环境下,Lua数据结构严格验证库
  15. ├── game
  16. ├── auth #每个服务对应的逻辑脚本目录
  17. ├── center
  18. ├── gate
  19. ├── node
  20. ├── room
  21. ├── service_auth.lua
  22. ├── service_center.lua
  23. ├── service_gate.lua
  24. ├── service_hub.lua
  25. ├── service_node.lua
  26. ├── service_room.lua
  27. ├── service_user.lua
  28. └── user
  29. ├── log # 运行日志目录
  30. ├── main_game.lua # game 进程启动文件
  31. ├── main_hub.lua # hub 进程启动文件
  32. ├── moon # moon源码
  33. ├── node.json # 节点配置文件,用于集群通信
  34. ├── protocol # protobuf协议定义目录
  35. ├── annotations.proto # 只用于生成Lua代码注解
  36. ├── center.proto # 定义自动转发到Center服务的消息
  37. ├── common.proto # 公共protobuf定义
  38. ├── json_verify.json # 用于verify_proto
  39. ├── proto.pb
  40. ├── room.proto # 定义自动转发到Room服务的消息
  41. └── user.proto # 定义自动转发到User服务的消息
  42. ├── robot
  43. └── robot.lua # 机器人脚本
  44. ├── serverconf.lua # 数据库相关配置
  45. ├── start_game.sh
  46. ├── start_hub.sh
  47. ├── start_server.bat
  48. ├── static
  49. ├── table # 游戏配表目录
  50. └── www # GM后台目录
  51. └── tools
  52. ├── Annotations.lua # 自动生成的代码注解文件
  53. ├── ProtoEnum.lua
  54. ├── __pycache__
  55. ├── make_annotations.py
  56. ├── moonfly.bat
  57. ├── moonfly.py # 代码注解生成脚本
  58. └── protoc3.exe

编译Server

  1. clone

    1. git clone --recursive https://github.com/sniper00/MoonDemo.git
  2. 参考moon编译

运行

  • 安装redis采用默认配置即可。如需要修改数据库配置, 可以修改server目录下的serverconf.lua
  • 如果想使用postgresql, 先修改postgresql使用md5验证登录, 修改server目录下的serverconf.lua中pgsql相关配置,main_game.lua 中取消db_game服务的注释,这样userdata数据会保存到postgresql中

  • windows使用 start_server.bat 脚本运行。linux和macos使用start_hub.sh,start_game.sh依次启动。默认会自动运行机器人服务。配表 可以修改机器人数量

  • 简易后台管理

  • 如需要自己部署,可以修改node.json中的ip地址

  • Client请使用unity2022 启动执行第一个场景Prepare。

调试

  • 安装vscode
  • 安装LuaPanda 插件
  • 在需要调试的服务第一行添加代码(作为示例,room服务第一行添加了这行代码)
    1. require("common.LuaPanda").start("127.0.0.1", 8818)
  • 配置调试器

image

  • F5启动vscode-LuaPanda调试器
  • 使用vscode,在该服务的逻辑代码出下断点
  • 运行服务器,触发断点处的逻辑

image

Client

客户端主要用来演示怎样使用 asyn/await 来处理网络消息,等异步操作。

  1. var v = await Network.Call<S2CLogin>(UserData.GameSeverID, new C2SLogin { openid = userName.text });
  2. if (v.ok)
  3. {
  4. UserData.time = v.time;
  5. UserData.username = userName.text;
  6. await Network.Call<S2CMatch>(UserData.GameSeverID, new C2SMatch {});
  7. SceneManager.LoadScene("MatchWait");
  8. }
  9. else
  10. {
  11. MessageBox.Show("auth failed");
  12. }
  13. //获取请求结果或者逻辑ErrorCode
  14. var callResult = await Network.Call<NetMessage.S2CWorld, NetMessage.S2CErrorCode>(UserData.GameSeverID, new NetMessage.C2SHello
  15. {
  16. Hello = "world"
  17. });
  18. if (!callResult.IsFirstResponse)
  19. {
  20. Debug.Log("error:" + callResult.Response2.Code);
  21. }
  1. //注册回调方式的网络消息
  2. Network.Register<NetMessage.S2CMatchSuccess>((res) =>
  3. {
  4. MessageBox.SetVisible(false);
  5. SceneManager.LoadScene("Game");
  6. });

开发流程

需要安装python3

添加新协议

修改serverprotocol目录下的proto文件,协议命名规则

  • C2Sxxxx表示客户端发送给服务器的消息;
  • S2Cxxxxx,SBCxxxxx 表示服务器发送给客户端的消息,其中SBC表示广播消息,只是为了便于区分。
  1. user.proto
  2. center.proto
  3. room.proto
  4. common.proto
  5. annotations.proto 只生成lua注解时使用

编写完成协议后,运行tools目录下的moonfly.bat,其它平台运行python3 moonfly.py

代码注解

protocol目录的文件都会生成lua注解,建议逻辑中多定义proto结构,提高开发速度,特别是复杂的对象,能达到 typescript 80% 的代码提示能力。对于关键数据可以用使用 verify_proto 进行验证,如需要存数据库的数据。

User逻辑开发流程示例

定义协议

如果是小游戏之类的客户端,不想采用protobuf作为通信协议,可以通过修改这里, 采用json作为通信协议。注意Demo自带的Unity客户端暂时不支持切换Json协议

protocol/user.proto 中添加, 然后运行tools/moonfly.bat

  1. //客户端发送
  2. message C2SHello
  3. {
  4. string hello = 1;
  5. }
  6. //服务器返回
  7. message S2CWorld
  8. {
  9. string world = 1;
  10. }

编写逻辑

game/user/目录下新建文件 “Hello.lua”

  1. Lua逻辑脚本标准定义
  1. local common = require "common"
  2. local GameCfg = common.GameCfg --游戏配置
  3. local ErrorCode = common.ErrorCode --逻辑错误码
  4. local CmdCode = common.CmdCode --客户端通信消息码
  5. ---@type user_context
  6. local context = ...
  7. local scripts = context.scripts ---方便访问同服务的其它lua模块
  8. local Hello = {}
  9. ---这里初始化本模块相关的数据
  10. function Hello.Init()
  11. -- local DB = scripts.UserModel.Get()
  12. -- if not DB.hello then
  13. -- DB.hello = {
  14. -- a = 1,
  15. -- b = 2
  16. -- }
  17. -- end
  18. end
  19. ---这里可以访问其它模块,做更多初始化工作
  20. function Hello.Start()
  21. --scripts.Item.AddItem(1,1,1)
  22. end
  23. return Hello
  1. 编写逻辑(完整代码)
  1. local moon = require("moon")
  2. local common = require "common"
  3. local GameCfg = common.GameCfg --游戏配置
  4. local ErrorCode = common.ErrorCode --逻辑错误码
  5. local CmdCode = common.CmdCode --客户端通信消息码
  6. ---@type user_context
  7. local context = ...
  8. local scripts = context.scripts ---方便访问同服务的其它lua模块
  9. ---@class Hello ---代码提示
  10. local Hello = {}
  11. ---这里初始化本模块相关的数据
  12. function Hello.Init()
  13. -- local DB = scripts.UserModel.Get()
  14. -- if not DB.hello then
  15. -- DB.hello = {
  16. -- a = 1,
  17. -- b = 2
  18. -- }
  19. -- end
  20. end
  21. ---这里可以访问其它模块,做更多初始化工作
  22. function Hello.Start()
  23. scripts.Item.AddItem(1,1,1)
  24. end
  25. ---注册服务间通信的消息处理函数
  26. ---其它服务可以访问`context.send_user(uid, "Hello.DoSometing1", 1)`
  27. function Hello.DoSometing1(params)
  28. ---访问内存中的数据库对象: MutGet会触发存库操作
  29. local data = scripts.UserModel.MutGet().hello
  30. data.a = 100
  31. end
  32. ---注册服务间通信的消息处理函数
  33. ---其它服务可以访问`local res = context.call_user(uid, "Hello.DoSometing1", 1)`
  34. function Hello.DoSometing2()
  35. ---访问内存中的数据库对象: Get用于只读,不会触发存库
  36. local data = scripts.UserModel.Get().hello
  37. return data.a
  38. end
  39. ---注册客户端消息处理函数
  40. ---@param req C2SHello
  41. function Hello.C2SHello(req)
  42. local cfg = GameCfg.item[1]
  43. if not cfg then
  44. return ErrorCode.ItemNotExist ---直接返回错误码, 会给玩家发送 S2CErrorCode 消息
  45. end
  46. context.S2C(CmdCode.S2CWorld, {world=req.hello}) ---给客户端发送消息
  47. end
  48. return Hello