斗地主-前端

簡介

https://github.com/GramYang/landlord_client

這是一個斗地主游戲的Android前端demo,對應(yīng)landlord_go后端。你可以把它當(dāng)作一個Android使用原生socket連接go的后端的范例。

特點(diǎn)

  • 使用原生socket+protobuf與后端通信,輕便簡捷珊随。

  • 牌桌采用自定義view設(shè)計(jì),更加簡潔(不會套殼)。

  • 個人寫前端沒什么設(shè)計(jì)天賦蝇更,頁面比較挫。宛逗。

自定義view

CircleImageView

看這里

CardsPack

繼承自recyclerview坎匿。

adapter

其中持有一個CardsPackAdapter,CardsPackAdapter持有兩個list代表持有的牌和打出去的牌,牌在點(diǎn)擊后從持有的牌中刪除并添加到打出去的牌中替蔬。

使用

在GameActivity中告私,首先初始化LinearLayoutManager:


LinearLayoutManager layoutManagerLeft = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL,

false);

layoutManagerLeft.setSmoothScrollbarEnabled(false);

然后將LinearLayoutManager和傳遞數(shù)據(jù)和標(biāo)記位的CardsPackAdapter傳遞進(jìn)CardsPack實(shí)例中:


rivalLeftCardsOut.setLayoutManager(layoutManagerLeft);

leftAdapter = rivalLeftCardsOut.new CardsPackAdapter(this, 1, leftCardsOut);

rivalLeftCardsOut.setAdapter(leftAdapter);

網(wǎng)絡(luò)庫

網(wǎng)絡(luò)庫在OkSocket的基礎(chǔ)上進(jìn)行了修改。

使用


ConnectionInfo loginInfo = new ConnectionInfo(Constants.loginHost, Constants.loginPort);

loginManager = OkSocket.open(loginInfo);

loginManager.registerReceiver(new LoginHandler());

loginManager.connect();

ConnectionInfo用來存放ip和port承桥,調(diào)用OkSocket.open()返回一個IConnectionManager實(shí)例驻粟,調(diào)用registerReceiver()傳入連接后的回調(diào)handler實(shí)例,最后調(diào)用connect()進(jìn)行socket的連接凶异。

  • LoginHandler

這是一個SocketActionAdapter的實(shí)現(xiàn)類蜀撑,SocketActionAdapter的實(shí)現(xiàn)類會在socket返回信息后分發(fā)時逐個遍歷,因此可以有多個SocketActionAdapter的實(shí)現(xiàn)類去實(shí)現(xiàn)相同的方法剩彬,他們都會被調(diào)用酷麦。

流程分析

OkSocket.open(info)

返回一個IConnectionManager實(shí)例,該實(shí)例以info為key存入一個map緩存喉恋,第一次調(diào)用的時候調(diào)用new ConnectionManagerImpl(info)沃饶。ConnectionManagerImpl構(gòu)造器中設(shè)置了一些標(biāo)記位,提取了ip和port轻黑。

ConnectionManagerImpl.connect()

  1. 綁定ActionHandler實(shí)例

ActionHandler實(shí)現(xiàn)了SocketActionAdapter的三個方法糊肤,被存入在一個列表中,在讀寫線程解析完數(shù)據(jù)后會使用這個列表來遍歷處理數(shù)據(jù)氓鄙。如何選擇不同的方法來處理不同情況下的數(shù)據(jù)馆揉,就是靠的key了,其實(shí)現(xiàn)在ActionDispatcher玖详。

這里的ActionHandler主要處理IOThread啟動把介、斷開,連接失敗蟋座。

  1. 綁定DefaultReconnectManager實(shí)例

DefaultReconnectManager同樣實(shí)現(xiàn)了ISocketActionListener拗踢,也算是handler的一種。

在attach方法中向臀,持有傳入的mConnectionManager技扼,以及其中的mPulseManager(這個時候可能是空的,但是過幾毫秒就不是空的了)瓜客,將DefaultReconnectManager實(shí)例也傳入到ISocketActionListener列表中谷扣。DefaultReconnectManager實(shí)現(xiàn)了ISocketActionListener的三個方法,主要實(shí)現(xiàn)了斷線重連的邏輯芹彬。

  1. 生成ConnectionManagerImpl.ConnectionThread實(shí)例蓄髓,并Start

其中,socket實(shí)例連接指定地址并設(shè)置超時舒帮,然后生成PulseManager實(shí)例会喝,生成IOThreadManager實(shí)例并startEngine()陡叠。連接成功就發(fā)送action_connection_success,拋異常則發(fā)送action_connection_failed和異常實(shí)例肢执。

  1. 生成PulseManager實(shí)例

上面生成了PulseManager的實(shí)例枉阵,其pulse()需要你在設(shè)置mSendable后自己手動調(diào)用。

在pulse()中预茄,設(shè)置好心跳頻率后兴溜,開啟mPulseThread,其線程模式是DUPLEX耻陕。run()中拙徽,超時了就斷開,否則就發(fā)送心跳包淮蜈,發(fā)送的動作是通過IConnectionManager實(shí)現(xiàn)的斋攀。

心跳是在一個線程中定期調(diào)用ConnectionManagerImpl的send方法實(shí)現(xiàn)的。

  1. IOThreadManager.startEngine()

IOThreadManager的構(gòu)造器持有socket的兩個流梧田,以及mActionDispatcher淳蔼。然后獲取mReaderProtocol,也就是ltv協(xié)議三個部分長度的解析方法裁眯。初始化ReaderImpl和WriterImpl鹉梨。

startEngine()中生成DuplexWriteThread和DuplexReadThread實(shí)例,然后啟動穿稳。

  1. DuplexWriteThread和DuplexReadThread

就是裝包和拆包的過程存皂,無限執(zhí)行WriterImpl和ReaderImpl的write()和read()。

IConnectionManager.send()

調(diào)用IOThreadManager的send()逢艘,向WriteImpl的LinkedBlockingQueue中添加消息旦袋,WriteImpl的write()則會不停的從LinkedBlockingQueue中取消息處理。

這里增加了一個sendAfterConnect()它改,用一個信號量控制在connect()中的邏輯執(zhí)行完后再異步的調(diào)用send()

ReaderImpl.read()

從socket的inputStream中讀取流數(shù)據(jù)后疤孕,拆包填充OriginalData,調(diào)用

mStateSender.sendBroadcast(IOAction.ACTION_READ_COMPLETE, originalData)

進(jìn)行分發(fā)央拖,這里的mStateSender就是ActionDispatcher祭阀。

ActionDispatcher的sendBroadcast()中將action和originalData存入

ActionDispatcher.ActionBean實(shí)例,然后存入LinkedBlockingQueue中

ActionDispatcher中的DispatchThread靜態(tài)實(shí)例啟動鲜戒,其run()中從LinkedBlockingQueue取出ActionBean實(shí)例专控,然后遍歷ActionDispatcher的mResponseHandlerList(就是上面封裝的ISocketActionListener列表)

ActionDispatcher.dispatchActionToListener()

"action_read_complete":ReaderImpl.read()解析成功后發(fā)送

"action_read_thread_start":DuplexReadThread的beforeLoop()

"action_write_thread_start":DuplexWriteThread的beforeLoop()

"action_read_thread_shutdown":DuplexReadThread的loopFinish()

"action_write_thread_shutdown":DuplexWriteThread的loopFinish()

"action_pulse_request":WriterImpl的write()

"action_write_complete":WriterImpl的write()

"action_disconnection":DisconnectThread運(yùn)行完之后

"action_connection_success":ConnectionThread連接成功

"action_connection_failed":ConnectionThread連接失敗拋出異常

游戲邏輯

  1. 進(jìn)入房間后準(zhǔn)備

準(zhǔn)備:發(fā)送new ReadyRequest(true)

解除整備:發(fā)送new CancelReadyRequest(true)

所有玩家準(zhǔn)備后開始搶地主

  1. 開始搶地主

onGrabLandlordResponse()中顯示三張地主牌,顯示手中的牌遏餐,顯示搶地主面板

選擇搶地主:選擇加倍

選擇不搶地主:順移到右邊的玩家搶地主伦腐,如果其他兩個玩家都不搶地主,強(qiáng)制成為地主失都,選擇加倍

  1. 選擇加倍

onEndGrabLandlordResponse()中地主顯示加倍選擇面板蔗牡,在地主選擇加倍后其他兩個玩家選擇加倍應(yīng)答面板

不選擇加倍:直接開始游戲

選擇加倍:其他玩家根據(jù)倍數(shù)判斷是否加倍颖系,有一個人不同意加倍,則加倍取消辩越,開始游戲

  1. 開始游戲

onCardsOutResponse()中出牌和接牌

對牌的判斷和處理邏輯都在前端

第一個牌出完的玩家觸發(fā)結(jié)束游戲

  1. 結(jié)束游戲

onEndGameResponse()中彈窗顯示戰(zhàn)績,你可以選擇繼續(xù)也可以選擇退出房間

與后端的協(xié)議適配

后端的拆包和封包協(xié)議都采用的是ltv格式信粮,length-type-value黔攒。內(nèi)容長度如下:


public class ReaderProtocol1 implements IReaderProtocol {

    @Override

    public int getHeaderLength() {

        return 2;

    }

    @Override

    public int getTypeLength() {

        return 2;

    }

    @Override

    public int getBodyLength(byte[] header, ByteOrder byteOrder) {

        if (header == null || header.length < getHeaderLength()) {

            return 0;

        }

        ByteBuffer bb = ByteBuffer.wrap(header);

        bb.order(byteOrder);

        return bb.getShort() - 2;

    }

}

封包示例


public class JsonReq implements ISendable {

    private int jsonType;

    private byte[] content;

    public JsonReq(int jsonType, byte[] content) {

        this.jsonType = jsonType;

        this.content = content;

    }

    public int getJsonType() {

        return jsonType;

    }

    public byte[] getContent() {

        return content;

    }

    @Override

    public byte[] parse() {

        Landlord.JsonREQ.Builder builder = Landlord.JsonREQ.newBuilder();

        builder.setJsonType(jsonType);

        builder.setContent(ByteString.copyFrom(content));

        Landlord.JsonREQ req = builder.build();

        byte[] body = req.toByteArray();

        ByteBuffer bb = ByteBuffer.allocate(4+body.length);

        bb.order(ByteOrder.LITTLE_ENDIAN);

        bb.putShort((short)(body.length+2));

        bb.putShort(Constants.JSON_REQ_TYPE);

        bb.put(body);

        return bb.array();

    }

}

特別注意:后端采用的是小端序列,因此前端也要用小端序列强缘,而Java默認(rèn)的是大端序列督惰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市旅掂,隨后出現(xiàn)的幾起案子赏胚,更是在濱河造成了極大的恐慌,老刑警劉巖商虐,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件觉阅,死亡現(xiàn)場離奇詭異,居然都是意外死亡秘车,警方通過查閱死者的電腦和手機(jī)典勇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叮趴,“玉大人割笙,你說我怎么就攤上這事∶幸啵” “怎么了伤溉?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長妻率。 經(jīng)常有香客問我乱顾,道長,這世上最難降的妖魔是什么舌涨? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任糯耍,我火速辦了婚禮,結(jié)果婚禮上囊嘉,老公的妹妹穿的比我還像新娘温技。我一直安慰自己,他們只是感情好扭粱,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布舵鳞。 她就那樣靜靜地躺著,像睡著了一般琢蛤。 火紅的嫁衣襯著肌膚如雪蜓堕。 梳的紋絲不亂的頭發(fā)上抛虏,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機(jī)與錄音套才,去河邊找鬼迂猴。 笑死,一個胖子當(dāng)著我的面吹牛背伴,可吹牛的內(nèi)容都是我干的沸毁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼傻寂,長吁一口氣:“原來是場噩夢啊……” “哼息尺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起疾掰,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤搂誉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后静檬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炭懊,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年巴柿,在試婚紗的時候發(fā)現(xiàn)自己被綠了凛虽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡广恢,死狀恐怖凯旋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钉迷,我是刑警寧澤至非,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站糠聪,受9級特大地震影響荒椭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜舰蟆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一趣惠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧身害,春花似錦味悄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春涨颜,著一層夾襖步出監(jiān)牢的瞬間费韭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工庭瑰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留星持,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓弹灭,卻偏偏與公主長得像钉汗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鲤屡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360