/moon

A cross-platform,lightweight,scalable game server framework written in C++, and support Lua Script

Primary LanguageC++MIT LicenseMIT

Moon Game Framework

moon 是一个轻量级的游戏服务器框架,底层采用C++编写,逻辑层主要采用Lua。框架使用多线程和多LuaVM(lua service)的模式, 把不同的游戏服务器模块(通常是一个进程),尽量集中到一个进程中,简化开发和维护难度。一个lua service可以表示一个游戏服务器模块,它们之间通过消息队列互相通信(大部分相情况下只是传递一个数据指针),通常比进程间IO通信高效,也不需要维护复杂的网络链接。 Lua的协程特性,也使异步逻辑的编写更加简单。

框架本身没有提供太多的游戏业务层逻辑,主要目标是提供基础功能。

如果有疑问或者建议欢迎提交issue,或者加入QQ群 QQ交流群: 543833695

目录

依赖

主要特性

  • async lua协程封装异步操作,取代传统的回调模式

  • timer 轮式定时器的实现,配合lua协程,更加方便处理定时任务

  • coroutine socket 协程socket的封装,方便编写自定义协议的网络模块

  • websocket 支持websocket协议

  • cluster 提供集群间通信(暂时通过统一的配置文件作为服务发现,不支持动态增删)

  • extensible 利用sol2库可以方便编写C/C++lua交互的扩展模块

编译

  1. 需要支持C++17的编译器不同平台安装支持C++17的编译器

  2. 获取源码

    git clone https://github.com/sniper00/moon.git
  • 编译
    • windows 双击运行 build.bat。如果失败,参考详细编译步骤
    • linux 运行 build.sh。如果没有可执行权限请自行添加:
          chmod +x build.sh
          chmod +x premake5
      
  1. 详细编译步骤
  • 获取最新的premake5。下载链接。选择自己的平台,把下载好的premake可执行文件复制到源码中premake5.lua同级目录。

  • 生成工程文件并编译

    • windows
        # vs2017 or vs2019
        premake5.exe vs2017
        # 打开visual studio编译
    • linux
        # 默认采用gcc编译,中括号表示可选项: clang
        ./premake5 gmake [--cc=clang]
        # 默认debug版本,中括号表示可选项: release版本
        make clean [config=release]
        make [config=release]
    • macosx
        # 只支持使用clang编译
        ./premake5 gmake --cc=clang
        # 默认debug版本,中括号表示可选项: release版本
        make clean [config=release]
        make [config=release]
  1. 运行
    # 直接用一个文件作为lua服务启动,方便快速测试
    ./moon -f lua_service_file
    # 采用配置文件启动,方便配置和管理大量服务,用于正式部署
    ./moon [-c config-file] -r server-id

Hello World

下面编写一个echo server功能的lua service:

local moon = require("moon")

local socket = require("moon.socket")

local HOST = "127.0.0.1"
local PORT = 9526

-------------------2 bytes len (big endian) protocol------------------------
socket.on("accept",function(fd, msg)
    print("accept ", fd, msg:bytes())
    socket.settimeout(fd, 10)
end)

socket.on("message",function(fd, msg)
    --echo message to client
    socket.write(fd, msg:bytes())
end)

socket.on("close",function(fd, msg)
    print("close ", fd, msg:bytes())
end)

socket.on("error",function(fd, msg)
    print("error ", fd, msg:bytes())
end)

local listenfd = 0

moon.start(function()
    listenfd = socket.listen(HOST, PORT, moon.PTYPE_SOCKET)
    socket.start(listenfd)--start accept
    print("server start ", HOST, PORT)
    print("enter 'CTRL-C' stop server.")
end)

moon.destroy(function()
    socket.close(listenfd)
end)

# 运行 echo server
./moon -f example/helloworld.lua
# 另启动一个终端运行 client(协程socket编写的客户端)
./moon -f example/helloworld_client.lua
# 输入任意字符

示例

示例代码可以在 example 目录找到

# 运行 server
./moon -f example/network.lua
# 另启动一个终端运行 client
./moon -f example/helloworld_client.lua
# 输入任意字符

网络-websocket协议

network_websocket.lua

--Echo Server Example
local moon = require("moon")

local socket = require("moon.socket")

local conf = ...

local HOST = conf.host or "127.0.0.1"
local PORT = conf.port or 12346

socket.wson("accept",function(fd, msg)
    print("wsaccept ", fd, msg:bytes())
    -- 设置read超时,单位秒
    socket.settimeout(fd, 10)
end)

socket.wson("message",function(fd, msg)
    -- binary frame
    -- socket.write(fd, msg)
    -- text frame
    socket.write_text(fd, msg:bytes())
end)

socket.wson("close",function(fd, msg)
    print("wsclose ", fd, msg:bytes())
end)

socket.wson("error",function(fd, msg)
    print("wserror ", fd, msg:bytes())
end)

socket.wson("ping",function(fd, msg)
    print("wsping ", fd, msg:bytes())
    socket.write_pong(fd,"my pong")
end)

local listenfd = 0
moon.start(function()
    listenfd = socket.listen(HOST, PORT, moon.PTYPE_SOCKET_WS)
    socket.start(listenfd)
    print("websocket server start ", HOST, PORT)
    print("enter 'CTRL-C' stop server.")
end)

moon.destroy(function()
    socket.close(listenfd)
end)

./moon -f example/network_websocket.lua
# 使用浏览器运行websocket_client.html

动态创建服务

create_service.lua

local moon = require("moon")

local conf = ...

if conf.slave then
    local command = {}

    command.QUIT = function ()
        print("recv quit cmd, bye bye")
        moon.quit()
    end

    local function docmd(sender,header,...)
    -- body
        local f = command[header]
        if f then
            f(sender,...)
        else
            error(string.format("Unknown command %s", tostring(header)))
        end
    end

    moon.start(function()
        print("conf:", conf.message)

        moon.dispatch('lua',function(msg,p)
            local sender = msg:sender()
            local header = msg:header()
            docmd(sender,header, p.unpack(msg))
        end)

        if conf.auto_quit then
            print("auto quit, bye bye")
            -- 使服务退出
            moon.quit()
        end
    end)
else
    moon.start(function()

        moon.async(function()
            -- 动态创建服务, 配置同时可以用来传递一些信息
            moon.new_service("lua", {
                name = "create_service",
                file = "create_service.lua",
                message = "Hello create_service",
                auto_quit = true
            })


            -- 动态创建服务,获得服务ID,方便用来通信
            local serviceid =  moon.new_service("lua", {
                name = "create_service",
                file = "create_service.lua",
                slave = true,
                message = "Hello create_service_coroutine"
            })

            print("new service",string.format("%X",serviceid))

            moon.send("lua", serviceid, "QUIT")

            moon.abort()
        end)
    end)

end
 ./moon.exe -f example/example_create_service.lua

服务间通信-Callback模式

example_callback.lua

local moon = require("moon")

local conf = ...

if conf.receiver then
    -----------------------------THIS IS RECEIVER SERVICE-------------------
    local command = {}

    command.PING = function(sender, ...)
        print(moon.name(), "recv ", sender, "command", "PING")
        print(moon.name(), "send to", sender, "command", "PONG")
        moon.send('lua', sender,'PONG', ...)
    end

    local function docmd(sender,header,...)
        -- body
        local f = command[header]
        if f then
            f(sender,...)
        else
            error(string.format("Unknown command %s", tostring(header)))
        end
    end

    moon.dispatch('lua',function(msg,p)
        local sender = msg:sender()
        local header = msg:header()
        docmd(sender,header, p.unpack(msg))
    end)

    print("callback example: service receiver start")
else
    -----------------------------THIS IS SENDER SERVICE-------------------
    local command = {}

    command.PONG = function(...)
        print(...)
        print(moon.name(), "recv ", "command", "PING")
        moon.abort()
    end

    local function docmd(header,...)
          local f = command[header]
          if f then
              f(...)
          else
              error(string.format("Unknown command %s", tostring(header)))
          end
    end

    moon.dispatch('lua',function(msg,p)
        local header = msg:header()
        docmd(header, p.unpack(msg))
    end)

    moon.start(function()
        print("callback example: service sender start")

        moon.async(function()
            local receiver =  moon.new_service("lua", {
                name = "callback_receiver",
                file = "example_callback.lua",
                receiver = true
            })

            print(moon.name(), "send to", receiver, "command", "PING")
            moon.send('lua', receiver,"PING","Hello")
        end)
    end)
end
 ./moon.exe -f example/example_callback.lua

服务间通信-协程模式

example_coroutine.lua

local moon = require("moon")
local conf = ...

if conf.receiver then
    local command = {}

    command.PING = function(sender,sessionid, ...)
        print(moon.name(), "recv ", sender, "command", "PING")
        print(moon.name(), "send to", sender, "command", "PONG")
        -- 把sessionid发送回去,发送方resume对应的协程
        moon.response("lua",sender,sessionid,'PONG', ...)
    end

    local function docmd(sender,sessionid,cmd,...)
        -- body
        local f = command[cmd]
        if f then
            f(sender,sessionid,...)
        else
            error(string.format("Unknown command %s", tostring(cmd)))
        end
    end

    moon.dispatch('lua',function(msg,p)
        local sender = msg:sender()
        -- sessionid 对应表示发送方 挂起的协程
        local sessionid = msg:sessionid()
        docmd(sender,sessionid, p.unpack(msg))
    end)

else
    moon.start(function()
        moon.async(function()
            local receiver =  moon.new_service("lua", {
                name = "example_coroutine",
                file = "example_coroutine.lua",
                receiver = true
            })

            print(moon.name(), "call ", receiver, "command", "PING")
            print(moon.co_call("lua", receiver, "PING", "Hello"))
            moon.abort()
        end)
    end)
end
 ./moon.exe -f example/example_coroutine.lua

定时器

example_timer.lua

local moon = require("moon")

moon.start(function()

    local count = 10
    moon.repeated(1000,count,function()
        print("repeate 10 times timer tick",count)
        count = count - 1
        if 0 == count then
            moon.abort()
        end
    end)

    moon.async(function()
        print("coroutine timer start")
        moon.sleep(1000)
        print("coroutine timer tick 1 seconds")
        moon.sleep(1000)
        print("coroutine timer tick 1 seconds")
        moon.sleep(1000)
        print("coroutine timer tick 1 seconds")
        moon.sleep(1000)
        print("coroutine timer tick 1 seconds")
        moon.sleep(1000)
        print("coroutine timer tick 1 seconds")
        print("coroutine timer end")
    end)
end)
 ./moon.exe -f example/example_timer.lua

使用配置文件的示例

config.json(example 目录)

配置文件说明,请参考wiki

测试用例

    ./moon -c example/config.json -r 1

Service Send Benchmark

服务间发送消息性能测试

    ./moon -c example/config.json -r 2

协程socket性能测试

协程socket编写的多线程服务端,采用redis-benchmark测试性能

    ./moon -c example/config.json -r 3

Socket性能测试

2字节大端表示长度的协议的网络性能测试

    ./moon -c example/config.json -r 4

编写一个Mysql服务,为其他服务提供访问Mysql DB的功能

    ./moon -c example/config.json -r 5

编写一个Redis服务,为其他服务提供访问Redis DB的功能

    ./moon -c example/config.json -r 6

sharetable 更新配表示例

    ./moon -c example/config.json -r 7

Cluster Example

进程间通信示例

    ./moon -c example/config.json -r 8
    ./moon -c example/config.json -r 9

游戏示例

Friend Open Source

NoahGameFrame

  • Author: ketoo
  • GitHub: https://github.com/ketoo/NoahGameFrame
  • Description: A fast, scalable, distributed game server framework for C++, include actor library, network library,can be used as a real time multiplayer game engine ( MMO RPG ), which plan to support C#/Python/Lua script, and support Unity3d, Cocos2dx, FlashAir client access.