[lua] 遊戲客戶端邏輯使用lua協程

  咱們的遊戲有這樣一種情景:客戶端中角色須要用到一些公會的數據,但服務器不會在玩家(創角後)一進入到遊戲裏就推送給玩家,而是須要客戶端本身在須要的時候向服務器請求公會的數據,以前的實現就是在請求消息的時候加一個回調函數,消息一回來就執行回調函數繼續後續的業務!但後面發現存在這樣的不足:服務器

1.有時會用到好多個參數,這就須要每次都把一些無關的參數傳給消息請求者,而後回調過來再把這些參數原封不動地傳回來,很繁瑣;app

2.這樣的回調寫法感受很不符合人類的順序思惟習慣,有點亂;函數

回調的lua代碼寫法相似:測試

--@callbackFunc帶的參數可能有(self, p1, p2, ..., [最後還有allianceData, 這個是取到數據後分發時加上的])

function AllianceProxy:requestAllianceData(callbackFunc)

  --handle send request msg

  --這裏使用一個uniqueKey往一個管理器中註冊保存這個回調

end

function AllianceProxy:responseAllianceData(allianceData)

  --handle receive response msg

  --這裏根據上面uniqueKey分發消息,執行回調, 調用相似:callbackFunc(self, p1, p2, ..., allianceData)

end

--請求公會數據的寫法就是這樣:

function Role:handleAllianceData()

  local function _callbackFunc(self, p1, p2, allianceData)

    --使用的到allianceData處理相關的業務邏輯

  end

  AllianceProxy:requestAllianceData(packing(_callbackFunc, self, p1, p2)) --packing返回的仍然是一個function

end

 

上述的參數self, p1, p2, ...等等徹底就不必往requestAllianceData中丟過去的,但就是由於收到回調後須要用到,做爲強迫症的我,以爲這種寫法很噁心,因此今天想到,這徹底能夠利用lua提供的協程機制來作這件事,大體思路的代碼以下:(注:下面這些代碼未通過編譯及測試,另外還未考慮協程恢復執行時若其主函數所在table被銷燬的狀況, 後續會完善下面的代碼)ui

--建立並運行一個協程
--@param mainFunc 協程主函數
function global:createAndRunningCo(mainFunc)
    self:assertFmt(type(mainFunc) == 'function', 'func=%s is not function', tostring(mainFunc))
    local co = coroutine.create(mainFunc)
    local isSuccess, errMsg = coroutine.resume(co, co)
    self:assertFmt(isSuccess, 'one error happened in mainFunc:%s', errMsg)
end

function global:checkCoIsRunning(co)
    self:assertFmt(type(co) == 'thread')
    local curCo = coroutine.running()
    self:assertFmt(co == curCo, 'co[%s] is not running, current running coroutine is %s, please sure that co is running', co, curCo)
end

function global:yieldAndSaveCo(key, co)
    self:assertFmt(key ~= nil)
    self:checkCoIsRunning(co)

    if coT[key] == nil then
        coT[key] = {}
    end

    table.insert(coT[key], co)

    return coroutine.yield()
end

function global:resumeAndRemoveCo(key, ...)
    if coT[key] then
        for _, co in ipairs(coT[key]) do
            if coroutine.status(co) == 'suspended' then
                local isSuccess, errMsg = coroutine.resume(co, ...)
                self:assertFmt(isSuccess, 'one error happened in mainFunc:%s', errMsg)
            end
        end
        coT[key] = nil
    end
end
local global = require('global')

global:createAndRunningCo(function(co)
    local allianceData = Alliance:requestAllianceData(co)

    --do something
end)

function AllianceProxy:requestAllianceData(co)
    --send msg : request alliance Data

    return global:yieldAndSaveCo('allianceData', co)
end

function AllianceProxy:responseAllianceData(allianceData)
    --receive msg : response alliance data

    global:resumeAndRemoveCo('allianceData', allianceData)
end

須要獲取公會數據的用戶就使用下面這樣的寫法則可:lua

global:createAndRunningCo(function(co) local allianceData = Alliance:requestAllianceData(co) --do something end)
而後在消息收發那裏作好相應的處理就好了,這個寫法跟前面的對比,很明顯,咱們順序的編寫相應的邏輯就好了,徹底不須要像前面的說等回調過來後反過頭來再執行相應的邏輯
相關文章
相關標籤/搜索