iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 22
1
Modern Web

三十天路邊賭場上線了!系列 第 22

DAY22 遊戲登陸串接

前言

今天我們來讓我們的遊戲畫面可以接上遊戲伺服器吧!

結構

我們大概就是把cst、cmd這兩個指令的代號搬過來,讓我們跟伺服器可以共用相同代號。
我們還新增了$io.ts這一用來管我們的socketio。

└─src
    │  App.ts
    │  cmd.ts
    │  cst.ts
    │  Game.ts
    ├─components
    │  ├─elements
    │  │      Animation.ts
    │  │      Sprite.ts
    │  │      Texture.ts
    │  │      Wrapper.ts
    │  │      WrapperContainer.ts
    │  │      WrapperContainerCenter.ts
    │  │      WrapperType.ts
    │  ├─groups
    │  │      Body.ts
    │  │      Casino.ts
    │  │      ChipBox.ts
    │  │      Loading.ts
    │  │      Navbottom.ts
    │  │      Navtop.ts
    │  │      Pokers.ts
    │  │      Table.ts
    │  └─objects
    │          AreaBetNumber.ts
    │          Bg.ts
    │          Chip.ts
    │          Countdown.ts
    │          CountdownNumber.ts
    │          Dealer.ts
    │          Desk.ts
    │          DeskHover.ts
    │          Info.ts
    │          InfoMoneyNumber.ts
    │          Poker.ts
    │          PokerPoint.ts
    │          PokerResult.ts
    │          PokerWin.ts
    │          TotalBetNumber.ts
    │          WaitNextBetNotify.ts
    ├─config
    │      chipType.ts
    │      imagePath.ts
    │      loadingPath.ts
    │      pokerPoint.ts
    │      pokerType.ts
    ├─loaders
    │      Loader.ts
    ├─services
    │      $io.ts
    ├─store
    │      actions.ts
    │      index.ts
    │      reducer.ts
    │      redux.ts
    │      store.ts
    └─utils
            tools.ts

Socketio

首先大致說明一下我們的$io.ts架構,因為是模擬http的req、res,所以必須要有timeout防止指令超時,另外直接把一些請求方法封裝在這,比較好可以直接使用,不然組件內會一堆emit、on,而目前token暫時直接使用id,之後正式版本會採用jwt的token,就會很安全了!

$io.ts
 
import io from 'socket.io-client'
import cmd from '@/cmd'

let $io = function () {
  let $io: any
  let token: string
  let timeout: number = 5000
  return {
    initalSocket: function (url: any) {
      token = localStorage.getItem('user') || '1'
      $io = io(url, {
        query: {
          token: token
        }
      })
    },
    on: function (cmd: string, listener: Function) {
      $io.on(cmd, listener)
    },
    emit: function (cmd: string) {
      let args = [].slice.call(arguments, 1)
      $io.emit(cmd, ...args)
    },
    REQ_USER_INFO: function (data?: any) {
      return new Promise((resolve, reject) => {
        let _timeout = setTimeout(reject, timeout);
        $io.emit(cmd.REQ_USER_INFO, data)
        $io.on(cmd.RES_USER_INFO, (data: any) => {
          resolve(data.result)
          clearTimeout(_timeout)
        })
      })
    },
    REQ_USER_LOGIN: function (data?: any) {
      return new Promise((resolve, reject) => {
        let _timeout = setTimeout(reject, timeout);
        $io.emit(cmd.REQ_USER_LOGIN, data)
        $io.on(cmd.RES_USER_LOGIN, (data: any) => {
          resolve(data.result)
          clearTimeout(_timeout)
        })
      })
    },
    REQ_USER_TB_SITDOWN: function (data?: any) {
      return new Promise((resolve, reject) => {
        let _timeout = setTimeout(reject, timeout);
        $io.emit(cmd.REQ_USER_TB_SITDOWN, { tbid: '1' })
        $io.on(cmd.RES_USER_TB_SITDOWN, (data: any) => {
          resolve(data.result)
          clearTimeout(_timeout)
        })
      })
    }
  }
}()

export default $io

初始化區域

這是Game.ts,一開始進入的地方,也是有loading頁面的地方,在這邊我們會先做基本的請求資料還有與伺服器說我們登入了,另外還有坐下這功能,代表我們不只登入了,還選了一張桌子坐下了!不過都是桌號1,因為我們目前只有一桌,但預留可以多開多桌的功能。

Game.ts

import Loader from '@/loaders/Loader'
import imagePath from '@/config/imagePath'
import Wrapper from '@/components/elements/Wrapper'
import WrapperContainer from '@/components/elements/WrapperContainer'
import loadingPath from '@/config/loadingPath'
import Casino from '@/components/groups/Casino'
import Loading from '@/components/groups/Loading'
import $io from '@/services/$io'
import { store, actions } from '@/store/index'

export default class Game {
  private _app: PIXI.Application
  private _game: Wrapper
  private _loading: Wrapper

  constructor() {
    this._app = new PIXI.Application({ width: 1625, height: 900 })
    this._game = new WrapperContainer()
    this._loading = new WrapperContainer()
    this._game.addChild(this._loading)
    this._app.stage.addChild(this._game.getContainer())
    document.body.appendChild(this._app.view)

    $io.initalSocket('http://localhost:3000')
    $io.on('connect', () => {
      console.log('connect')
    })

    // 先load載入頁面
    this.loadimage(loadingPath)
      .then(() => {
        // 再load所有圖片
        return this.loadimage(imagePath, 0)
      })
      .then(() => {
        return this.loadInfo()
      }).then(() => {
        return this.initalInfo()
      }).then(() => {
        this.setup()
      })
  }

  private setup(): void {
    let casino = new Casino()
    this._game.addContainer(casino.getContainer())
  }

  private loadimage(imagePath: any, delay?: number): Promise<any> {
    return new Promise((resolve, reject) => {
      try {
        let loading = new Loading()
        this._loading.addChild(loading)
        Loader.load(imagePath)
          .on((e: number) => {
            loading.update(e * 100)
          })
          .then(() => {
            return loading.done(delay)
          })
          .then(() => {
            this._loading.removeChildren()
            resolve()
          })
      } catch (err) {
        reject(err)
      }
    })
  }


  private initalInfo() {
    return new Promise((resolve, reject) => {
      Promise.all([$io.REQ_USER_LOGIN(), $io.REQ_USER_TB_SITDOWN(), $io.REQ_USER_INFO()]).then((res: any) => {
        console.log(res[0])
        console.log(res[1])
        console.log(res[2])
        store.dispatch(actions.updateBalance({ balance: res[2].balance }))
        resolve()
      })
    })
  }
  private loadInfo() {
    return new Promise((resolve, reject) => {
      let user = localStorage.getItem('user')
      resolve()
    })
  }
}

Table內的socket事件

這只是Table的一部分,我們收到TB就是Table的通知的時候,會找到對應的代號,並且更新要更新的值,像現在有的功能是更新桌的倒數計時。
還有如果超過三局沒有下注,是會被踢出的,因為遊戲如果沒有人在玩的時候(坐下),就會休息,那超過三局是防止有人掛機,害伺服器一直發牌。

Table.ts

constructor() {
    super()
// ...以上省略 

$io.on(cmd.MSG_TB_NTF, (reason: any, data: any) => {
      switch (reason) {
        case cst.TB_NTF_COUNTDOWN_START:
          console.log('cst.TB_NTF_COUNTDOWN_START')
          break
        case cst.TB_NTF_COUNTDOWN_STOP:
          console.log('cst.TB_NTF_COUNTDOWN_STOP')
          break
        case cst.TB_NTF_COUNTDOWN_TIME:
          this._countdown.updateCountdown(data.countdown)
          break
        case cst.TB_NTF_FANPI:
          console.log('cst.TB_NTF_FANPI')
          break
        case cst.TB_NTF_PI_RESULT:
          console.log('cst.TB_NTF_PI_RESULT')
          break
        case cst.TB_NTF_STR_JOIN:
          console.log('cst.TB_NTF_STR_JOIN')
          break
        case cst.TB_NTF_STR_BETOUT:
          console.log('cst.TB_NTF_STR_BETOUT')
          break
        case cst.TB_NTF_STR_QUIT:
          console.log('cst.TB_NTF_STR_QUIT')
          break
        case cst.TB_NTF_KICKOUT:
          alert('被遊戲踢出!')
          console.log('cst.TB_NTF_KICKOUT')
          break
      }
    })  
// ...以下省略
}

後記

今天大概就是這樣,沒想到串一下也花了我好多時間,因為伺服器出了點問題,但大致上串一串應該是OK,過兩天應該可以直接部屬在Heroku上給大家玩了!


上一篇
DAY21 伺服器端資料串接
下一篇
DAY23 串接倒數更新、串接下注
系列文
三十天路邊賭場上線了!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言