Goでゲームのロビー機能のようなモノを作る〜宿題の提出1

はじめに

Go2 Advent Calendar 2020 9日目の記事の宿題の提出です。

この記事では、ゲームのマッチングを行うロビー機能をGoで作って紹介しました。
記事の時点ではWebSocketを管理するhubをpoolに戻す処理が未対応で宿題となっていました。
今回はこの部分を作ってきました。

github.com/bluemon0919/lobby

サーバに/huboutというハンドラを追加しています。 クライアントがアクセスするとhubからプレイヤー情報を削除します。

func main() {
    flag.Parse()
    http.HandleFunc("/", serveFront)
    http.HandleFunc("/lobby", serveLobby)
    http.HandleFunc("/play", servePlay)
    http.HandleFunc("/login", serveLoginHandler)
    http.HandleFunc("/ws", serveWebsocket)
    http.HandleFunc("/hubout", huboutHandler) // 追加
    err := http.ListenAndServe(*addr, nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

huboutHandler()はCookieから自身のセッションIDを取得し、セッションIDをHubManagerから削除します。

func huboutHandler(w http.ResponseWriter, r *http.Request) {
    manager := sessions.NewManager()
    session, err := manager.Get(r, cookieName)
    if err != nil {
        fmt.Println(err)
        return
    }
    hubManger := websocket.NewManager()
    hubManger.Destroy(session.ID) // WebSocketのHubManagerから削除する
}

HubManagerが管理しているデータは次の通り。
Destroy()はこれらのデータから対象となるキーを削除していきます。

type Manager struct {
    database map[string]*Hub
    pool     []*Hub
    count    map[*Hub]int
    users    map[*Hub][]string
}

プレイヤーを削除するごとにm.countをデクリメントすると、クロスタイミングでhubを横取りされる可能性があるので プレイヤー全員を削除したタイミングでm.countも削除するようにしています。 最後に、使っていたhubをpoolに戻します。これで次のプレイヤーが新たにhubを使えるようになりました。

func (m *Manager) Destroy(key string) {
    if hub, exist := m.database[key]; exist {
        delete(m.database, key)
        users := m.Users(hub)
        for u := 0; u < len(users); u++ {
            if users[u] == key {
                m.users[hub] = remove(m.users[hub], u)
                users = m.users[hub]
            }
        }
        if 0 == len(users) {
            // ペアが解放されたらhubをpoolに戻す
            delete(m.count, hub)
            m.pool = append(m.pool, hub)
        }
    }
}