{"id":20345285,"url":"https://github.com/sniper00/moondemo","last_synced_at":"2025-04-04T17:05:20.354Z","repository":{"id":48165025,"uuid":"136702278","full_name":"sniper00/MoonDemo","owner":"sniper00","description":"moon game server的一个使用示例，搭建简单的房间类游戏服务器框架","archived":false,"fork":false,"pushed_at":"2025-03-13T15:16:57.000Z","size":14658,"stargazers_count":167,"open_issues_count":0,"forks_count":39,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-03-28T16:09:35.264Z","etag":null,"topics":["cpp","game","game-server","lua","network","unity"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sniper00.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-06-09T07:47:29.000Z","updated_at":"2025-03-25T02:06:11.000Z","dependencies_parsed_at":"2024-05-19T10:27:23.499Z","dependency_job_id":"b8675de2-a481-4403-927d-50df85fa2210","html_url":"https://github.com/sniper00/MoonDemo","commit_stats":null,"previous_names":["sniper00/ballgame"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sniper00%2FMoonDemo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sniper00%2FMoonDemo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sniper00%2FMoonDemo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sniper00%2FMoonDemo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sniper00","download_url":"https://codeload.github.com/sniper00/MoonDemo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247217172,"owners_count":20903008,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","game","game-server","lua","network","unity"],"created_at":"2024-11-14T22:07:40.145Z","updated_at":"2025-04-04T17:05:20.318Z","avatar_url":"https://github.com/sniper00.png","language":"C#","readme":"# 项目简介\n\n多人简易版球球大作战，游戏服务器框架[moon](https://github.com/sniper00/moon)的一个使用示例。\n主要演示\n- 游戏服管理大量客户端网络连接\n- `One Player One Service`模式,处理玩家非共享状态,提高单服承载能力和更加Clean的内存管理\n- 异步`Redis`连接池服务的使用\n- 使用`sharetable`管理和更新游戏配表\n- 客户端消息自动注册和处理\n- 服务间消息自动注册和通信\n- 游戏逻辑编写规范\n- 服务器集群搭建\n- 服务器简易管理后台\n- 代码热更\n- 代码注解\n- 使用vscode LuaPanda 插件调试服务器代码\n- 使用vscode lua-language-server 插件提供lua代码智能能提示:\n    vscode打开server目录，安装[`lua-language-server`](https://marketplace.visualstudio.com/items?itemName=sumneko.lua)插件，即可获得代码提示能力，如果没有代码提示，在.vscode目录下, 创建或者修改settings.json, 添加\n    ```json\n        {\n            \"Lua.workspace.library\": [\n                \"./moon/lualib/\",\n                \"./moon/service/\"\n            ]\n        }\n    ```\n\n-------\n\n\u003cdiv align=\"center\"\u003e\n\n![Server Message Processing](https://github.com/sniper00/BallGame/raw/master/image/message.svg)\n\n服务器消息处理\n\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n![Server Message Processing](https://github.com/sniper00/BallGame/raw/master/image/start.png)\n\n登陆界面，可选Tcp或者Websocket\n\n\u003c/div\u003e\n\n\n\u003cdiv align=\"center\"\u003e\n\n![Server Message Processing](https://github.com/sniper00/BallGame/raw/master/image/game.png)\n\n游戏画面\n\n\u003c/div\u003e\n\n# Server结构介绍\n\nHub Server:\n1. 提供服务后台管理, 服务器节点配置管理(支持动态开启新服)\n2. 提供http server 和 telnet两种协议接口\n\nGame Server开启了7种服务:\n- node 处理hub的请求消息，常用于对接SDK和服务器后台管理\n- cluster 服务器集群通信节点服务\n- gate 负责管理玩家网络连接，并转发玩家网络消息到对应玩家服务\n- auth 负责登录，创建、删除、离线加载、玩家服务\n- center 负责玩家匹配逻辑，动态创建room服务\n- user 玩家服务，一个服务对应一个玩家，处理玩家消息，管理玩家私有状态。 与其它玩家交互的消息转发到room服务。\n- room 游戏场景服务，简易球球大作战玩法逻辑\n\n目录结构\n```shell\n.\n├── common/                 #逻辑公共模块目录\n│   ├── CmdCode.lua         #自动生成的和客户端通信的协议定义文件\n│   ├── Database.lua        #数据库操作\n│   ├── ErrorCode.lua       #逻辑错误码定义\n│   ├── GameCfg.lua         #游戏配表\n│   ├── GameDef.lua         #游戏内部相关配置\n│   ├── LuaPanda.lua        #LuaPanda调试库\n│   ├── init.lua            #common目录初始化脚本\n│   ├── protocol.lua        \n│   ├── protocol_pb.lua     #protobuf消息编码和解码逻辑\n│   ├── setup.lua           #All In One包装: 消息注册，服务内模块注册，配表更新，代码热更\n│   ├── vector2.lua\n│   └── verify_proto.lua    #开发环境下，Lua数据结构严格验证库\n├── game\n│   ├── auth                #每个服务对应的逻辑脚本目录\n│   ├── center\n│   ├── gate\n│   ├── node\n│   ├── room\n│   ├── service_auth.lua\n│   ├── service_center.lua\n│   ├── service_gate.lua\n│   ├── service_hub.lua\n│   ├── service_node.lua\n│   ├── service_room.lua\n│   ├── service_user.lua\n│   └── user\n├── log                     # 运行日志目录\n├── main_game.lua           # game 进程启动文件\n├── main_hub.lua            # hub 进程启动文件\n├── moon                    # moon源码\n├── node.json               # 节点配置文件，用于集群通信\n├── protocol                # protobuf协议定义目录\n│   ├── annotations.proto   # 只用于生成Lua代码注解\n│   ├── center.proto        # 定义自动转发到Center服务的消息\n│   ├── common.proto        # 公共protobuf定义\n│   ├── json_verify.json    # 用于verify_proto\n│   ├── proto.pb\n│   ├── room.proto          # 定义自动转发到Room服务的消息\n│   └── user.proto          # 定义自动转发到User服务的消息\n├── robot\n│   └── robot.lua           # 机器人脚本\n├── serverconf.lua          # 数据库相关配置\n├── start_game.sh\n├── start_hub.sh\n├── start_server.bat\n├── static      \n│   ├── table               # 游戏配表目录\n│   └── www                 # GM后台目录\n└── tools\n    ├── Annotations.lua     # 自动生成的代码注解文件\n    ├── ProtoEnum.lua       \n    ├── __pycache__\n    ├── make_annotations.py\n    ├── moonfly.bat\n    ├── moonfly.py          # 代码注解生成脚本\n    └── protoc3.exe\n```\n\n# 编译Server\n\n1. clone\n```\ngit clone --recursive https://github.com/sniper00/MoonDemo.git\n```\n\n2. [参考moon编译](https://github.com/sniper00/moon#build)\n\n# 运行\n\n- 安装`redis`采用默认配置即可。如需要修改数据库配置, 可以修改server目录下的`serverconf.lua`。\n- 如果想使用postgresql, 先修改postgresql使用md5验证登录, 修改server目录下的`serverconf.lua`中pgsql相关配置，`main_game.lua` 中取消`db_game`服务的注释，这样userdata数据会保存到postgresql中\n\n- windows使用 `start_server.bat` 脚本运行。linux和macos使用`start_hub.sh`,`start_game.sh`依次启动。默认会自动运行机器人服务。[配表](https://github.com/sniper00/BallGame/blob/master/server/static/table/constant.lua) 可以修改机器人数量\n\n- 简易后台管理\n    - 方式一 Web http://127.0.0.1:8003/\n    - 方式二 `telnet 127.0.0.1 8003`, 输入 `S1 help`\n\n- 如需要自己部署，可以修改`node.json`中的ip地址\n\n- Client请使用unity2022 启动执行第一个场景Prepare。\n\n# 调试\n\n- 安装`vscode`\n- 安装LuaPanda 插件\n- 在需要调试的服务第一行添加代码(作为示例，room服务第一行添加了这行代码)\n```lua\nrequire(\"common.LuaPanda\").start(\"127.0.0.1\", 8818)\n```\n- [配置调试器](https://github.com/Tencent/LuaPanda/blob/master/Docs/Manual/access-guidelines.md#%E5%BC%80%E5%A7%8B%E8%B0%83%E8%AF%95)\n\n![image](https://github.com/sniper00/BallGame/raw/master/image/setting.png)\n\n- F5启动vscode-LuaPanda调试器\n- 使用vscode,在该服务的逻辑代码出下断点\n- 运行服务器，触发断点处的逻辑\n\n![image](https://github.com/sniper00/BallGame/raw/master/image/debug.png)\n\n# Client\n\n客户端主要用来演示怎样使用 asyn/await 来处理网络消息，等异步操作。\n```csharp\nvar v = await Network.Call\u003cS2CLogin\u003e(UserData.GameSeverID, new C2SLogin { openid = userName.text });\nif (v.ok)\n{\n    UserData.time = v.time;\n    UserData.username = userName.text;\n    await Network.Call\u003cS2CMatch\u003e(UserData.GameSeverID, new C2SMatch {});\n    SceneManager.LoadScene(\"MatchWait\");\n}\nelse\n{\n    MessageBox.Show(\"auth failed\");\n}\n\n//获取请求结果或者逻辑ErrorCode\nvar callResult = await Network.Call\u003cNetMessage.S2CWorld, NetMessage.S2CErrorCode\u003e(UserData.GameSeverID, new NetMessage.C2SHello\n{\n    Hello = \"world\"\n});\n\nif (!callResult.IsFirstResponse)\n{\n    Debug.Log(\"error:\" + callResult.Response2.Code);\n}\n```\n\n```csharp\n//注册回调方式的网络消息\nNetwork.Register\u003cNetMessage.S2CMatchSuccess\u003e((res) =\u003e\n{\n    MessageBox.SetVisible(false);\n    SceneManager.LoadScene(\"Game\");\n});\n```\n\n# 开发流程\n\n\n需要安装`python3`\n\n\n## 添加新协议\n\n修改`server`的`protocol`目录下的proto文件，协议命名规则\n- `C2Sxxxx`表示客户端发送给服务器的消息；\n- `S2Cxxxxx`,`SBCxxxxx` 表示服务器发送给客户端的消息，其中`SBC`表示广播消息，只是为了便于区分。\n\n```shell\nuser.proto\ncenter.proto \nroom.proto \ncommon.proto\nannotations.proto 只生成lua注解时使用\n\n```\n\n编写完成协议后，运行tools目录下的`moonfly.bat`,其它平台运行`python3 moonfly.py`\n\n## 代码注解\n\nprotocol目录的文件都会生成lua注解，建议逻辑中多定义proto结构，提高开发速度，特别是复杂的对象，能达到 typescript 80% 的代码提示能力。对于关键数据可以用使用 verify_proto 进行验证，如需要存数据库的数据。\n\n## User逻辑开发流程示例\n\n### 定义协议\n\n\u003e 如果是小游戏之类的客户端，不想采用`protobuf`作为通信协议，可以通过[修改这里](https://github.com/sniper00/MoonDemo/blob/master/server/common/init.lua#L6), 采用`json`作为通信协议。**注意Demo自带的Unity客户端暂时不支持切换Json协议**。\n\n在 `protocol/user.proto` 中添加, 然后运行`tools/moonfly.bat`\n```proto\n//客户端发送\nmessage C2SHello\n{\n    string hello = 1;\n}\n\n//服务器返回\nmessage S2CWorld\n{\n    string world = 1;\n}\n```\n\n### 编写逻辑\n\n在`game/user/`目录下新建文件 \"Hello.lua\"\n\n1. Lua逻辑脚本标准定义\n\n```lua\nlocal common = require \"common\"\nlocal GameCfg = common.GameCfg --游戏配置\nlocal ErrorCode = common.ErrorCode --逻辑错误码\nlocal CmdCode = common.CmdCode --客户端通信消息码\n\n---@type user_context\nlocal context = ...\nlocal scripts = context.scripts ---方便访问同服务的其它lua模块\n\nlocal Hello = {}\n\n---这里初始化本模块相关的数据\nfunction Hello.Init()\n    -- local DB = scripts.UserModel.Get()\n    -- if not DB.hello then\n    --     DB.hello = {\n    --         a = 1,\n    --         b = 2\n    --     }\n    -- end\nend\n\n---这里可以访问其它模块,做更多初始化工作\nfunction Hello.Start()\n    --scripts.Item.AddItem(1,1,1)\nend\n\nreturn Hello\n```\n\n2. 编写逻辑(完整代码)\n\n```lua\nlocal moon = require(\"moon\")\nlocal common = require \"common\"\nlocal GameCfg = common.GameCfg --游戏配置\nlocal ErrorCode = common.ErrorCode --逻辑错误码\nlocal CmdCode = common.CmdCode --客户端通信消息码\n\n---@type user_context\nlocal context = ...\nlocal scripts = context.scripts ---方便访问同服务的其它lua模块\n\n---@class Hello ---代码提示\nlocal Hello = {}\n\n---这里初始化本模块相关的数据\nfunction Hello.Init()\n    -- local DB = scripts.UserModel.Get()\n    -- if not DB.hello then\n    --     DB.hello = {\n    --         a = 1,\n    --         b = 2\n    --     }\n    -- end\nend\n\n---这里可以访问其它模块,做更多初始化工作\nfunction Hello.Start()\n    scripts.Item.AddItem(1,1,1)\nend\n\n---注册服务间通信的消息处理函数\n---其它服务可以访问`context.send_user(uid, \"Hello.DoSometing1\", 1)`\nfunction Hello.DoSometing1(params)\n    ---访问内存中的数据库对象: MutGet会触发存库操作\n    local data = scripts.UserModel.MutGet().hello\n    data.a = 100\nend\n\n---注册服务间通信的消息处理函数\n---其它服务可以访问`local res = context.call_user(uid, \"Hello.DoSometing1\", 1)`\nfunction Hello.DoSometing2()\n    ---访问内存中的数据库对象: Get用于只读，不会触发存库\n    local data = scripts.UserModel.Get().hello\n    return data.a\nend\n\n---注册客户端消息处理函数\n---@param req C2SHello\nfunction Hello.C2SHello(req)\n    local cfg = GameCfg.item[1]\n    if not cfg then\n        return ErrorCode.ItemNotExist ---直接返回错误码, 会给玩家发送 S2CErrorCode 消息\n    end\n    context.S2C(CmdCode.S2CWorld, {world=req.hello}) ---给客户端发送消息\nend\n\nreturn Hello\n```\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsniper00%2Fmoondemo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsniper00%2Fmoondemo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsniper00%2Fmoondemo/lists"}