Unity3D的toLua+PMServer(醒醒,你的PMServer又报Error了)

Farmework With PG.ToLua And PMServer

Introduction

在游戏开发中,Lua由于其简洁规范的语法的特点得以广泛使用,在引进Luajit后,脚本语言的性能问题也得以解决,而ToLua的插件则给出了Lua与C#交互的解决方案。C#实现与Unity3D交互的内核框架,Lua脚本编写业务逻辑,ToLua插件进行两者的交互,这套客户端开发体系逐渐成熟。对于服务器端,则使用与Lua契合度高的C++编写内核框架,Lua实现服务器脚本逻辑。本文主要是对使用ToLua与PMServer实现Demo过程的一点记录。

PMServer Architecture

PMServer是一个游戏开发的服务器端引擎,内核框架封装了网络通信,配置文件与脚本解析,数据库操纵等一些底层接口。而开发人员通过编写规定格式的业务逻辑脚本来实现游戏逻辑。

在组成上,PMServer主要有以下部分:

  • mgrapp。负责对数据库的操作,其他app的管理,调试的接口
  • loginapp。主要处理登陆相关的逻辑,选择gateapp
  • gateapp。在登陆完毕之后连接的网关服务器,之后客户端所有的交互都与gateapp进行。它是客户端和gameapp之间的桥梁
  • gameapp。游戏中所有的逻辑和Entity都存在于gameapp中,一般会有多个gameapp。在服务器中各种对象都是以Entity的形式存在的,例如各种管理类PlayerMgr、SpaceMgr都是一个Entity,玩家的Player、Monster等也是Entity。

PMServer Deploy

使用PMServer的第一步就是部署了,老实说,我对服务器还是接触的不较少,部署的时候走了不少弯路,在这里做个流程的总结吧。

服务器的部署大致步骤如下:

  • 申请服务器开发权限,并使用XShell连接至服务器
  • 下载服务器端逻辑脚本并上传至服务器
  • 编辑配置文件(/server/bin/config.ini)
  • 设置其中的对外开放的端口与IP(登录的服务器IP相同)
  • 注意端口不能和别人冲突(在这里我卡了好久,启动服务器时一直报端口拒绝访问)
  • 设置客户端连接逻辑的IP和端口
  • ./start.sh

PMServer Business Develop

游戏的服务器端逻辑开发基于Lua脚本,主要流程如下:

  • 在defs/entities.xml中定义要实现的entity
  • 创建相应的entity.xml,并按格式填写属性与方法
  • 在script下实现entity.lua的游戏逻辑

其中,entity代表了游戏中的每个对象,客户端与服务器端的属性同步自动进行。
对于每个entity,在客户端中也要像服务器端这样操作,不同的只是Lua脚本中游戏逻辑的实现。

Login Realize

登录的逻辑实现主要是客户端Login_Panel与服务器端loginapp的交互。实现的思路主要如下:

  • 在客户端的登录按钮绑定登录事件
function LoginCtrl:initButton()
    print(type(self.view.button))
    self.view.button.actionClick = function()
            self:initServerState()
            self:connectToLoginServer()
    end
end
  • 在客户端登录实现中发送用户输入的用户名密码
function LoginCtrl:connectToLoginServer()

    local model = self.model
    if model:isDisconnected("login") then
        gamelog.info("connectToLoginServer")
        model:setConnecting("login")
        ClientUtils.connectServer(model.loginIP, model.loginPort, true)
    elseif model:isConnected("login") then
        gamelog.info("login isConnected")
        if not model:isConnected("gate") then
            model:setConnecting("gate")
            ClientUtils.callServerReliable("login",{["username"]=self.view.InputField.text, ["password"]=self.view.InputPwd.text},"")
        end
    end
end
  • 在服务器loginapp中实现验证逻辑,对客户端进行digest验证
function cs.proxy.login(clientId, extraInfo, digest)
    print(string.format("login: clientId[%s] username[%s] password[%s] digest[%s] ip[%s]",
        clientId, tostring(extraInfo.username), tostring(extraInfo.password), digest, pg.global.clientIpMap[clientId]))

    local cs_stub = cs.stub(clientId)

    if not pg.global.enableLogin then
        cs_stub.onServerNotReady()
        return
    end

    if type(extraInfo) == "table" then
        local username, secretkey = unpack(extraInfo)
        if secretkey == (pg.config.all.bot_login_key or "Cnr9,gMmqq$6Aaxsx=y<cd%#E3jBNQ") and username:starts("bot") then
            print("bot login ", username)
            login.loginSucc(clientId, username, nil, nil, "")
            return
        end
    end

    if extraInfo.password ~= getPwd(extraInfo.username) then
        print("password incorrect!")
        cs_stub.onPasswordError()
        return
    else
        print("verify correct!")
    end

    if digest ~= pg.def_digest then
        gamelog.info(string.format("def digest mismatch server[%s] client[%s]", pg.def_digest, digest))
    end

    if gameconfig.getConfig("enableMockLogin") then
        if type(extraInfo) == "table" then
            local username = extraInfo.username
            login.loginSucc(clientId, username, nil, nil, "")
            return
        end
    end
end
  • 当验证不通过,如密码错误时,则通过cs.stub调用客户端的回调函数
function LoginCtrl:onPasswordError()
    gamelog.error("Password Incorrect")
end
  • 验证成功后,则通过loginapp连接到gateapp并请求Token返回给客户端,客户端则根据返回的数据实现下一步逻辑,这里主要是通过客户端框架调用C#中的ResMgr跳转场景
function LoginCtrl:onLoginSucc()
    pg.global.resMgr:LoadLevel(nextScene, true)
    pg.global.ui:close(UIConst.UI_ID_LOGIN)
end

这样就大致实现了登录的逻辑

Cambat System

在之前的介绍中,已经做过一个单机版本的战斗场景了,这里主要是扩展之前的场景,通过C#调用Lua接口实现服务器与客户端的数据同步

  • 首先在场景的onLoaded中创建各个Entity
function CambatScene:onLoaded()
    for k,v in ipairs(heroEntity) do
        pg.global.entityMgr:CreateEntity(v, Avatar.Avatar)
    end

    for k,v in ipairs(monsterEntity) do
        pg.global.entityMgr:CreateEntity(v, Monster.Monster)
    end

    for k,v in ipairs(dummyEntity) do
        pg.global.entityMgr:CreateEntity(v, Dummy.Dummy)
    end
end
  • 在客户端对应的entity.lua下实现实体类
  • 与动画层面的交互主要通过为每个实体封装了一个pg.global.entity的全局接口,而后通过C#调用Appfacade.instance.Calllua来实现用户操作事件的相应数据逻辑,动画表现则在原先的C#脚本中实现,如用户点击地面移动角色的流程如下:
1. AppFacade.instance.CallLua("pg.global.avatar.move", position); //C#的移动逻辑中调用实体接口
2. Avatar:Move(position) --设置Avator的位置,使其同步到服务器
  • 由于服务器可以将entity数据持久化,所以当用户下一次登录时,则可以在C#的start()中通过调用上述的全局接口来获得服务器中的持久化数据(客户端调用serverMethod中定义的方法),从而实现如继续上次位置这样的功能
curPosition = AppFacade.instance.CallLua("pg.global.avatar.getCurPosition", null);
transform.position = curPosition;
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值