MoonDemo Multiplayer Game Server

repository·master·Indexed 41 days ago

https://github.com/sniper00/moondemo

A multiplayer game server demonstration built on the 'moon' framework. It showcases high-concurrency management, the 'One Player One Service' pattern, Redis integration, and automated protocol handling. The architecture consists of a Hub Server and various Game Server services including node, cluster, gate, auth, center, user, and room.

Tokens
2.3K
Snippets
6
Records
7
Agent score
2%

What's inside MoonDemo

  1. Understand the MoonDemo Server Architecture

    master

    The server follows a distributed architecture using the moon framework. It consists of a Hub Server and several Game Server services:

    Hub Server

    • Provides backend management and node configuration (supports dynamic scaling).
    • Interfaces via HTTP and Telnet.

    Game Server Services

    • node: Handles Hub requests (SDK/Management integration).
    • cluster: Manages inter-server cluster communication.
    • gate: Manages player network connections and forwards messages to player services.
    • auth: Handles login and player service lifecycle (creation/deletion/loading).
    • center: Manages player matching and dynamic room creation.
    • user: One Player One Service pattern. Handles individual player state and messages. Forwards interactions to room services.
    • room: Handles game scene logic (e.g., the actual ball game mechanics).
  2. Add new protocols to the server

    master

    To add new communication protocols:

    1. Modify the .proto files in the server/protocol/ directory. Use the following naming conventions:

      • C2Sxxxx: Client-to-Server messages.
      • S2Cxxxxx: Server-to-Client messages.
      • SBCxxxxx: Server-to-Client Broadcast messages.
    2. Run the protocol generator to update Lua code and annotations:

      • Windows: Run tools/moonfly.bat.
      • Linux/macOS: Run python3 tools/moonfly.py.
    python3 tools/moonfly.py
  3. Compile and Run the Server

    master

    Compilation

    1. Clone the repository recursively:
      git clone --recursive https://github.com/sniper00/MoonDemo.git
    2. Follow the moon build instructions.

    Running

    • Prerequisites: Install redis with default configuration. To use postgresql, modify serverconf.lua and uncomment the db_game service in main_game.lua.
    • Windows: Run start_server.bat.
    • Linux/macOS: Run start_hub.sh and start_game.sh sequentially.
    • Management:
      • Web: http://127.0.0.1:8003/
      • Telnet: telnet 127.0.0.1 8003 (use S1 help for commands).
    • Deployment: Modify node.json to set custom IP addresses for cluster communication.
    git clone --recursive https://github.com/sniper00/MoonDemo.git
  4. Implement User logic in Lua

    master

    To implement logic for a specific user service, create a file in game/user/ (e.g., Hello.lua). The script follows a standard structure using a context object and a scripts table for module access.

    Standard Script Template

    local moon = require("moon")
    local common = require "common"
    local GameCfg = common.GameCfg
    local ErrorCode = common.ErrorCode
    local CmdCode = common.CmdCode
    
    ---@type user_context
    local context = ...
    local scripts = context.scripts
    
    ---@class Hello
    local Hello = {}
    
    function Hello.Init()
        -- Initialization logic
    end
    
    function Hello.Start()
        -- Start-up logic
    end
    
    -- Handle Client Messages
    ---@param req C2SHello
    function Hello.C2SHello(req)
        -- Logic here
        context.S2C(CmdCode.S2CWorld, {world=req.hello})
    end
    
    -- Handle Inter-service Calls (Synchronous)
    function Hello.DoSometing2()
        local data = scripts.UserModel.Get().hello
        return data.a
    end
    
    -- Handle Inter-service Calls (Asynchronous/Fire-and-forget)
    function Hello.DoSometing1(params)
        local data = scripts.UserModel.MutGet().hello
        data.a = 100
    end
    
    return Hello

    Key Concepts for Logic Implementation:

    • context.S2C(cmd, data): Sends a message to the client.
    • context.call_user(uid, method, args): Calls a method on another player's service (synchronous).
    • context.send_user(uid, method, args): Sends a message to another player's service (asynchronous).
    • scripts.ModuleName.Get(): Read-only access to in-memory database objects.
    • scripts.ModuleName.MutGet(): Mutable access to in-memory database objects (triggers a save to DB on modification).
    local moon = require("moon")
    local common = require "common"
    local GameCfg = common.GameCfg --游戏配置
    local ErrorCode = common.ErrorCode --逻辑错误码
    local CmdCode = common.CmdCode --客户端通信消息码
    
    ---@type user_context
    local context = ...
    local scripts = context.scripts ---方便访问同服务的其它lua模块
    
    ---@class Hello ---代码提示
    local Hello = {}
    
    function Hello.Init()
        -- 这里初始化本模块相关的数据
    end
    
    function Hello.Start()
        scripts.Item.AddItem(1,1,1)
    end
    
    ---注册服务间通信的消息处理函数
    ---其它服务可以访问`context.send_user(uid, "Hello.DoSometing1", 1)`
    function Hello.DoSometing1(params)
        ---访问内存中的数据库对象: MutGet会触发存库操作
        local data = scripts.UserModel.MutGet().hello
        data.a = 100
    end
    
    ---注册服务间通信的消息处理函数
    ---其它服务可以访问`local res = context.call_user(uid, "Hello.DoSometing2", 1)`
    function Hello.DoSometing2()
        ---访问内存中的数据库对象: Get用于只读,不会触发存库
        local data = scripts.UserModel.Get().hello
        return data.a
    end
    
    ---注册客户端消息处理函数
    ---@param req C2SHello
    function Hello.C2SHello(req)
        local cfg = GameCfg.item[1]
        if not cfg then
            return ErrorCode.ItemNotExist ---直接返回错误码, 会给玩家发送 S2CErrorCode 消息
        end
        context.S2C(CmdCode.S2CWorld, {world=req.hello})
    end
    
    return Hello
  5. Configure Lua language server for code intelligence

    master

    To enable intelligent code completion in VSCode for the moondemo server, install the lua-language-server extension. If code hints are not appearing, create or modify .vscode/settings.json in the server directory to include the moon library paths:

    {
        "Lua.workspace.library": [
            "./moon/lualib/",
            "./moon/service/"
        ]
    }
    {
        "Lua.workspace.library": [
            "./moon/lualib/",
            "./moon/service/"
        ]
    }
  6. Debug Lua server code with LuaPanda

    master

    To debug server logic using VSCode and the LuaPanda plugin:

    1. Install the LuaPanda extension in VSCode.
    2. Add the following line to the top of the service script you wish to debug (e.g., game/room/your_script.lua):
      require("common.LuaPanda").start("127.0.0.1", 8818)
    3. Configure your VSCode debugger according to the LuaPanda manual.
    4. Press F5 to start debugging, set breakpoints, and trigger logic by running the server.
    require("common.LuaPanda").start("127.0.0.1", 8818)
  7. Use async/await for network messages in Unity Client

    master

    The C# client uses await Network.Call<TResponse>(...) to handle request-response patterns and Network.Register<TResponse>(callback) for asynchronous callbacks.

    Request-Response Example:

    var v = await Network.Call<S2CLogin>(UserData.GameSeverID, new C2SLogin { openid = userName.text });
    if (v.ok) {
        // handle success
    } else {
        // handle failure
    }

    Callback Registration Example:

    Network.Register<NetMessage.S2CMatchSuccess>((res) =>
    {
        MessageBox.SetVisible(false);
        SceneManager.LoadScene("Game");
    });
    var v = await Network.Call<S2CLogin>(UserData.GameSeverID, new C2SLogin { openid = userName.text });
    if (v.ok)
    {
        UserData.time = v.time;
        UserData.username = userName.text;
        await Network.Call<S2CMatch>(UserData.GameSeverID, new C2SMatch {});
        SceneManager.LoadScene("MatchWait");
    }
    else
    {
        MessageBox.Show("auth failed");
    }
    
    //获取请求结果或者逻辑ErrorCode
    var callResult = await Network.Call<NetMessage.S2CWorld, NetMessage.S2CErrorCode>(UserData.GameSeverID, new NetMessage.C2SHello
    {
        Hello = "world"
    });
    
    if (!callResult.IsFirstResponse)
    {
        Debug.Log("error:" + callResult.Response2.Code);
    }
    
    //注册回调方式的网络消息
    Network.Register<NetMessage.S2CMatchSuccess>((res) =>
    {
        MessageBox.SetVisible(false);
        SceneManager.LoadScene("Game");
    });