cloud-game/handler/handlers.go
2019-05-11 19:46:24 +08:00

170 lines
4 KiB
Go

package handler
import (
"io/ioutil"
"log"
"net/http"
"github.com/giongto35/cloud-game/cws"
storage "github.com/giongto35/cloud-game/handler/cloud-storage"
"github.com/giongto35/cloud-game/handler/gamelist"
"github.com/giongto35/cloud-game/handler/room"
"github.com/giongto35/cloud-game/webrtc"
"github.com/gorilla/websocket"
uuid "github.com/satori/go.uuid"
)
const (
gameboyIndex = "./static/gameboy.html"
debugIndex = "./static/index_ws.html"
)
// Flag to determine if the server is overlord or not
var upgrader = websocket.Upgrader{}
type Handler struct {
// Client that connects to overlord
oClient *OverlordClient
// Rooms map : RoomID -> Room
rooms map[string]*room.Room
// ID of the current server globalwise
serverID string
// isDebug determines the mode handler is running
isDebug bool
// Path to game list
gamePath string
// All webrtc peerconnections are handled by the server
// ID -> peerconnections
peerconnections map[string]*webrtc.WebRTC
// onlineStorage is client accessing to online storage (GCP)
onlineStorage *storage.Client
}
// NewHandler returns a new server
func NewHandler(overlordConn *websocket.Conn, isDebug bool, gamePath string) *Handler {
onlineStorage := storage.NewInitClient()
return &Handler{
oClient: NewOverlordClient(overlordConn),
rooms: map[string]*room.Room{},
peerconnections: map[string]*webrtc.WebRTC{},
isDebug: isDebug,
gamePath: gamePath,
onlineStorage: onlineStorage,
}
}
// GetWeb returns web frontend
func (h *Handler) GetWeb(w http.ResponseWriter, r *http.Request) {
indexFN := ""
if h.isDebug {
indexFN = debugIndex
} else {
indexFN = gameboyIndex
}
bs, err := ioutil.ReadFile(indexFN)
if err != nil {
log.Fatal(err)
}
w.Write(bs)
}
// WS handles normal traffic (from browser to host)
func (h *Handler) WS(w http.ResponseWriter, r *http.Request) {
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("[!] WS upgrade:", err)
return
}
defer c.Close()
client := NewBrowserClient(c)
sessionID := uuid.Must(uuid.NewV4()).String()
wssession := &Session{
ID: sessionID,
BrowserClient: client,
OverlordClient: h.oClient,
peerconnection: webrtc.NewWebRTC(),
handler: h,
}
defer wssession.Close()
if wssession.OverlordClient != nil {
wssession.RouteOverlord()
go wssession.OverlordClient.Heartbeat()
go wssession.OverlordClient.Listen()
}
wssession.RouteBrowser()
log.Println("oclient : ", h.oClient)
wssession.BrowserClient.Send(cws.WSPacket{
ID: "gamelist",
Data: gamelist.GetEncodedGameList(h.gamePath),
}, nil)
wssession.BrowserClient.Listen()
// TODO: Use callback
// listen to socket done to close peerconnection
go func() {
for {
<-client.Done
h.detachPeerConn(wssession.peerconnection)
return
}
}()
}
// Detach peerconnection detach/remove a peerconnection from current room
func (h *Handler) detachPeerConn(pc *webrtc.WebRTC) {
log.Println("Detach peerconnection")
roomID := pc.RoomID
room := h.getRoom(roomID)
if room == nil {
return
}
room.CleanSession(pc)
}
// getRoom returns room from roomID
func (h *Handler) getRoom(roomID string) *room.Room {
room, ok := h.rooms[roomID]
if !ok {
return nil
}
return room
}
// createNewRoom creates a new room
// Return nil in case of room is existed
func (h *Handler) createNewRoom(gameName string, roomID string, playerIndex int) *room.Room {
// If the roomID is empty,
// or the roomID doesn't have any running sessions (room was closed)
// we spawn a new room
if roomID == "" || !h.isRoomRunning(roomID) {
room := room.NewRoom(roomID, h.gamePath, gameName, h.onlineStorage)
// TODO: Might have race condition
h.rooms[room.ID] = room
return room
}
return nil
}
// isRoomRunning check if there is any running sessions.
// TODO: If we remove sessions from room anytime a session is closed, we can check if the sessions list is empty or not.
func (h *Handler) isRoomRunning(roomID string) bool {
// If no roomID is registered
room, ok := h.rooms[roomID]
if !ok {
return false
}
return room.IsRunning()
}