日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

00003 不思议迷宫.0006:客户端的操作如何反应到服务器?

發布時間:2023/12/3 综合教程 37 生活家
生活随笔 收集整理的這篇文章主要介紹了 00003 不思议迷宫.0006:客户端的操作如何反应到服务器? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.



00003 不思議迷宮.0006:客戶端的操作如何反應到服務器?

玩家點擊手機屏幕,根據點到內容的不同而執行不同的操作,比如切換畫面或者場景、播放動畫或聲音、發送數據等等。我現在所關心的是點到物品,比如主界面中的海怪觸手、漂流瓶、罐子等等,還有地牢中神龍許愿點擊99次礦物后才出現的鉆石。

我在主界面的創建代碼中未能找到海怪觸手、漂流瓶、罐子之類的相關代碼(可能有,但被我忽略了;也可能是確實沒有,它們都是動態創建的),所以研究下地牢中的物品撿取吧。

地牢界面的入口代碼為/src/game/ui/form/dungeon/UIDungeonMain.luac。打開瞅瞅,發現代碼很長,那如何快速找到我們所關心的物品撿取事件呢?其實辦法很簡單,只要搜索就行。

所謂物品撿取事件,其實也就是屏幕點擊事件,只不過點擊到的是物品。在cocos2dx中,點擊事件需要通過addTouchEventListener、addClickEventListener之類的函數進行注冊。查找結果讓我有點意外:

-- 注冊點擊事件

functionUIDungeonMain:registerTouchEvent()

??? -- 卷軸按鈕

??? local btn_Magic =findChildByName(self.node, "CT2/juanzhou");

??? local function onMagicOnClick(sender,eventType)

??????? if eventType == ccui.TouchEventType.endedthen

???????????AudioM.playFx("button_spell");

?

??????????? if ME.user.forbidToOpenMagicUI andnot DungeonGuideM.isGuideFinished() then

??????????????? -- 禁止打開魔法書界面

??????????????? return;

??????????? end

?

??????????? -- 打開魔法書界面

??????????? self:showMagicScrollUI();

??????? end

??? end

??? AddTouchEventListener(btn_Magic,onMagicOnClick);

?

??? -- 寶物按鈕

??? local btnTreasure =findChildByName(self.node, "CT2/baowu");

??? ……

??? AddTouchEventListener(btnTreasure,onTreasureOnClick);

?

??? -- 英雄格子

?? ?localbgHero = findChildByName(self.node, "CT2/bg3");

??? ……

??? AddTouchEventListener(bgHero,onBgHeroOnClick);

?

??? -- 施法選擇目標背景

??? local screen_bg =self.node:getChildByName("select_target_bg")

??? ……

??? screen_bg:addTouchEventListener(onOnClick);

?

? ??-- 稱號冒泡點擊事件

??? local careerBubble =findChildByName(self.node, "CT2/career_bubble/bg");

??? ……

???careerBubble:addTouchEventListener(onBubbleClick);

end

和撿取物品一點關系都沒有!

好吧,那就換個思路。想想,既然是撿取物品,那相關的函數的函數名或者函數代碼中總應當出現item、equipment這些字樣吧?這次確實找到了:

??? -- 注冊撿取物品的處理函數

???EventMgr.register("UIDungeonMain", event.PICK_UP_ITEM,function(params)

???????self.grids[params.pos]:onPickUp(params.bonus, params.newBonus,

???????????params.isItemVisible, params.noAlert, nil, params.borrowGrid);

??????? localpos = params.pos;

??????? localtype = params.type;

?

??????? -- 如果是拾取地圖

??????? if type== GRID_TYPE_MAP then

??????????? -- 判斷鄰格是否開啟

???????????local adjoinGrids = DungeonM.getAdjoinGrids(pos);

??????????? fori = 1, #adjoinGrids do

???????????????local targetPos = adjoinGrids[i];

???????????????local ok = DungeonM.canOpenGrid(targetPos);

???????????????if ok == GRID_OPEN_OK then

???????????????????self.grids[targetPos]:gotoVisible();

???????????????end

??????????? end

??????? end

?

???????self:whenPickUpItem(params);

??????? -- 更新界面UI

???????--self:updateUI();

??? end);

拾取地圖這什么鬼?先不管它,還是研究下self.grids[params.pos]:onPickUpself:whenPickUpItem

找到self.grids的賦值處,確定self.grids[params.pos]的類型:

??? -- 生成格子

??? self.grids ={}

??? for i = 1,GRID_ROW_NUM do

??????? for j =1, GRID_COL_NUM do

??????????? ……

???????????local grid = UIGrid.create(……);

??????????? ……

???????????self.grids[index] = grid;

??????????? ……

??????? end

??????? ……

??? end

UIGrid.create。再看看UIGrid.onPickUp:

-- 道具撿取回調函數

function UIGrid:onPickUp(bonus, newBonus,isItemVisible, noAlert, curNum, borrowGrid)

??? ……

??? if bonus[1]== 1 then

??????? -- 物品

??????? localitemId = bonus[2];

??????? localnum = bonus[3];

?

??????? localitemName = ItemM.query(itemId, "name");

??????? ……

?

??????? localitemsFlyIn;

??????? localdelayFly = false

??????? ……

???????itemsFlyIn = function()

??????????? -- 飛入效果

???????????local fileName = ItemM.query(itemId, "icon");

???????????local iconPath = getItemIconPath(fileName);

??????????? ifSpellM.isSpell(itemId) then -- 卷軸

??????????????? ……

???????????elseif EquipM.isEquipment(itemId) then -- 寶物

??????????????? ……

???????????elseif DragonWishM.isWishItem(itemId) then -- 龍珠

??????????????? ……

???????????elseif SkyResourceM.query(itemId) then -- 天空物資

??????????????? ……

???????????elseif PropertyM.isProperty(itemId) then?? -- 道具

???????????????AudioM.playFx("pick_goods");

???????????????pickupPropertyEffect(self, UIDungeonMgr.getCurLevel():getEquipNode(),iconPath, itemId, num);

??????????? else-- 其他

???????????????if not noAlert then

???????????????????str = itemName;

???????????????end

???????????????local itemBar = uiCurLevel:getFreeItemBar();

???????????????local iconNode = itemBar:getChildByName("icon");

???????????????local textNode = itemBar:getChildByName("num");

???????????????local total = (nil == curNum) and ItemM.getAmount(ME.user, itemId) orcurNum;

???????????????gainSpecialItemEffect(self, iconNode, iconPath, textNode, total, num);

???????????????AudioM.playFx("pick_goods");

??????????? end

???? ???end

?

??????? -- 不需要延遲,直接播放飛的效果

??????? if notdelayFly then

???????????itemsFlyIn();

??????? end

??????? ……

??? end

?

??? ……

end

僅僅是播放相關特效,并不涉及物品數量的處理。那么物品數量的處理在self:whenPickUpItem中?

-- 拾取物品的回調

function UIDungeonMain:whenPickUpItem(args)

??? local dungeonId= DungeonM.getDungeonId();

??? local layer= DungeonM.currentLayer();

?

??? for _,taskInfo in ipairs(DailyTaskM.getTaskList()) do

??????? iftaskInfo.dungeon_id == dungeonId and taskInfo.floor == layer then

??????????? ……

??????? end

??? end

end

真是失望,還是沒有。只能看看event.PICK_UP_ITEM,是在哪里被觸發的了。

在src目錄中搜索包含“PICK_UP_ITEM”的文件,然后發現了DungeonM.luac中內容:

-- 拾取物品

function pickUp(pos)

??? ……

?

??? -- 格子

??? local grids= getCurrentDungeon();

??? local grid =grids[pos];

??? local bonus= grid.bonus;

?

??? ……

??? -- 獎勵清除

??? grid.bonus =nil;

??? grid.picked= true;

?

??? ……

?

??? -- 增加行為

??? -- 這一句必須放在doBonus之前,以保證命令的先后順序(doBonus中也會觸發事件)

??? addAction({ ["cmd"] = "pick_item",["pos"] = pos, })

?

??? localresult;

??? -- 如果是立即使用的道具,那么使用掉

??? -- 應該在ProertyM里面做的,而且已經有現成的功能(配置auto_use便可)。by panyl

??? if bonus[1]== 1 and type(bonus[2]) == "number" then

??????? ……

??? else

??????? -- 獎勵

??????? result = BonusM.doBonus(bonus, "pick_item");

??? end

?

??? -- !!!!!!!!!!!!!!!!!!!!!!!!

??? --? 這里事件必須在獎勵之后做。因為拾取第一顆龍珠時,會去抽取許愿選項,

??? -- 需要抽取隨機數,等等,如果不放在獎勵之后就會導致先后順序問題,而且拾取龍珠/抽取許愿還不在同一個回合中

??? -- 一個回合事件

???EventMgr.fire(event.COMBAT_ROUND, { ["pos"] = pos,["isDelay"] = true });

?

??? -- 事件

??? EventMgr.fire(event.PICK_UP_ITEM, {["bonus"]= bonus, ["pos"] = pos, ["newBonus"] = result,["type"] = grid.type, ["class"] = grid.class, });

?

??? -- 嘗試完成成就:獲得競技場對手物品

???EventMgr.fire(event.GET_ARENA_ENEMY_ITEM, {["bonus"] = bonus,["pos"] = pos, });

?

???Profiler.funcEnd("pickUp");

??? return true,true;

end

“EventMgr.fire(event.PICK_UP_ITEM”是我們尋找的內容,但它不重要,因為我們已經知道它只干界面上的事。我們需要弄明白在它之前發生了啥。閱讀了代碼之后,有兩句話引起了我們的注意:

??? addAction({ ["cmd"] = "pick_item",["pos"] = pos, })

??? result = BonusM.doBonus(bonus, "pick_item");

在addAction的參數中,我看到了“cmd”,這個詞和網絡相關,看起來需要重點關注。

-- 地牢探索行為

function addAction(action, delay)

??? ……

??? -- 加入延時action

??? if delaythen

???????table.insert(delayAction, action);

??????? return;

??? end

??? ……

??? -- 第一個字節存放id,第二個字節pos,接著四個字節存放操作數據,最后兩個字節存放操作累計次數,共8個字節

??? localactionId = DungeonActionM.query(action.cmd, "id");

??? localpos? = action.pos or 0;

??? local data =action.data or 0;

?

??? local num =#actionCache;

?

??? -- 看下是否需要合并,如果前六個字節一樣(id、pos、data)則需要合并次數

??? if isSame…… then

??????? combine(……)

??????? return;

??? end

?

??? -- 新插入的一個操作

??? -- 8個字節

??? local buf =Buffer.create(8);

?

??? -- id

???Buffer.set8(buf, 1, actionId);

?

??? -- pos

???Buffer.set16(buf, 2, pos);

?

??? -- data

???Buffer.set32(buf, 4, data);

?

??? -- times

???Buffer.set16(buf, 8, (action["times"] or 1));

?

???table.insert(actionCache, buf);

?

???addDelayAction();

end

addAction函數將參數作了轉化,然后保存在actionCache中,最后調用了一次addDelayAction,這是為毛?

function addDelayAction()

??? localtoAction = delayAction;

??? delayAction= {};

?

??? for _,action in pairs(toAction) do

???????addAction(action);

??? end

end

在addAction函數中,如果指定了第二個參數為true,則第一個參數action將被保存到緩存而非actionCache中,緩存的內容直到下次調用addAction(x, false)時才會被保存到actionCache中。

actionCache在sync函數中是被使用:

-- 同步所有操作

function sync()

??? ……

??? -- 如果有緩存的操作

???Operation.cmd_dungeon_action(dungeonContainer.identify, actionCache);

?

??? -- 清空緩存

?? ?actionCache = {};

??? ……

end

應當是發送給服務器端的,網絡流程和上一章中的數據驗證應該是一樣的,不過我們還是驗證一下。Operation.cmd_dungeon_action:

function Operation.cmd_dungeon_action(identify,actions)

??? -- 當前層

??? local layer= DungeonM.currentLayer();

?

??? -- 最后的屬性

???DungeonLogM.collectFinalData(layer);

?

??? -- 行為隊列是空的

??? if #actions<= 0 then

??????? return;

??? end

?

??? -- 把所有操作都連接成一個buffer

??? local buf =Buffer.create(0);

??? for _,action in pairs(actions) do

??????? buf =Buffer.append(buf, action);

??? end

?

??? -- 獲取隨機數游標

??? local randomCursor= RandomFactoryM.packRandomCursor();

?

??? -- 等待應答id(一個唯一的id)

??? local authId= os.time();

?

??? local v = {

???????["identify"]??? =identify,

???????["auth_id"]???? =authId,

???????["layer"]?????? = layer,

???????["cursor"]????? =randomCursor.value,

???????["args"]??????? =buf.value,

???????["attrib"]????? =SimpleEncryptM.collectAttribCoding(ME.user),

??? };

?

??? -- 等待隊列

???DungeonServiceM.addWaitSync("CMD_DUNGEON_ACTION", v);

end

actions被轉換到連續的空間中,然后和其他一些參數一起,傳遞給DungeonServiceM.addWaitSync。其中有個參數["attrib"]????? =SimpleEncryptM.collectAttribCoding(ME.user),顯得可疑。

DungeonServiceM.addWaitSync函數:

-- 緩存同步操作消息,如果沒有收到服務器的應答就重發

function addWaitSync(cmd, msg,only_one)

??? local id = msg.auth_id;

??? msg.only_one = only_one;

??? queue[id] = { cmd, msg, };

?

??? -- 先發一次

??? reSendMsg();

?

??? -- 定時重連

???ScheduleM.createScheme("DungeonServiceM", reSendMsg,SYNC_MSG_REPOST_TIME, true)

end

reSendMsg函數:

-- 重發消息

function reSendMsg()

??? local keys =table.keys(queue);

??? ……

??? -- 如果消息已經空了或者已經離開游戲了

??? if notME.isInGame or (#keys <= 0) then

??????? -- 移除定時器

???????clearMsgQue();

?

??????? return;

??? end

?

??? -- 如果網絡沒連接上就不管了

??? ……

?

??? -- 不能同步

??? ……

?

??? -- 發送每條同步消息,按照id排序

???table.sort(keys);

??? for _, id inpairs(keys) do

??????? ifqueue[id] then

???????????local cmd = queue[id][1];

???????????local v?? = queue[id][2];

???????????SyncM.addMessage(cmd, v);

??????? end

?

??????? -- 如果是僅發一次,刪除

??????? ifqueue[id][2].only_one then

???????????queue[id] = nil;

??????? end

??? end

?

??? SyncM.startSync();

end

在Operation.cmd_dungeon_action中,傳遞了authId = os.time();該值在DungeonServiceM.addWaitSync中被用作queue的key。在reSendMsg中又根據key對queue進行了排序。簡單說,queue是按照時間先后的順序進行排序的。之后,就按照順序依次傳遞給SyncM.addMessage。

SyncM.addMessage僅將數據存到內部緩存:

-- 添加一條同步消息

function addMessage(cmd, para)

???table.insert(todoMessages, { cmd, para });

end

在reSendMsg函數的最后,調用SyncM.startSync進行真正的同步操作:

-- 開始進行同步

function startSync()

??? -- 判斷

??? if notcanSync() then

??????? return;

??? end

?

??? local socket= require "socket";

??? lastTime =socket.gettime();

?

??? -- 將todoMessages的消息內容剪切出來

??? messages =table.append(messages, todoMessages);

??? todoMessages= {};

??? if(#messages == 0) then

??????? -- 沒有任何數據需要同步

??????? lastTime= -999;

??????? return;

??? end

?

??? -- 先鎖住

??? locked =true;

?

??? -- 發送消息給服務器,開始同步

???trace("SyncM", "開始進行同步,版本號(%d),消息數量:%d", sync, #messages);

??? Communicate.send("CMD_START_SYNC", { sync =sync });

??? for _, v inipairs(messages) do

??????? Communicate.send(v[1], v[2]);

??? end

??? Communicate.send("CMD_END_SYNC", { sync =sync });

end

最后是幾個Communicate.send調用。它的詳情,在前面介紹過,此處不再贅述。

同步完成后還有一個CMD_END_SYNC回調:

-- 同步完畢

function endSync(success)

??? success =iif(success == nil, true, success);

???trace("SyncM", "同步完成,當前同步版本號:%d",ME.user.dbase:query("sync", 0));

??? sync =ME.user.dbase:query("sync", 0);

??? messages ={};

??? if (notsuccess) then

?????? ?-- 同步失敗了后續的東西也不需要再同步了,直接丟棄以服務器為準

???????todoMessages = {};

??? end

??? ……

??? -- 解鎖

??? locked =false;

??? ……

end

在服務器接收到CMD_START_SYNC之后,就開始處理我們的各種點擊事件了(在(x,y)位置撿取物品z)。

DungeonM.pickUp函數中的另一個惹人注意的語句“result =BonusM.doBonus(bonus, "pick_item");”我們已經無需理會了。它做的應當是客戶端的邏輯,有興趣的可以自行驗證。

總結

以上是生活随笔為你收集整理的00003 不思议迷宫.0006:客户端的操作如何反应到服务器?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。