mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-07-20 16:54:25 +00:00
update architecture
This commit is contained in:
parent
38bbd16a83
commit
37254852b3
13 changed files with 1136 additions and 719 deletions
195
'
vendored
Normal file
195
'
vendored
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/giongto35/cloud-game/config"
|
||||
"github.com/giongto35/cloud-game/cws"
|
||||
"github.com/giongto35/cloud-game/webrtc"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
// OverlordClient maintans connection to overlord
|
||||
// We expect only one OverlordClient for each server
|
||||
type OverlordClient struct {
|
||||
*cws.Client
|
||||
peerconnections map[string]*webrtc.WebRTC
|
||||
}
|
||||
|
||||
// NewOverlordClient returns a client connecting to overlord for coordiation between different server
|
||||
func NewOverlordClient(oc *websocket.Conn) *OverlordClient {
|
||||
if oc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
oclient := &OverlordClient{
|
||||
Client: cws.NewClient(oc),
|
||||
peerconnections: map[string]*webrtc.WebRTC{},
|
||||
}
|
||||
return oclient
|
||||
}
|
||||
|
||||
// RouteOverlord are all routes server received from overlord
|
||||
func (s *Session) RouteOverlord() {
|
||||
iceCandidates := [][]byte{}
|
||||
oclient := s.OverlordClient
|
||||
|
||||
// Received from overlord the serverID
|
||||
oclient.Receive(
|
||||
"serverID",
|
||||
func(response cws.WSPacket) (request cws.WSPacket) {
|
||||
// Stick session with serverID got from overlord
|
||||
log.Println("Received serverID ", response.Data)
|
||||
s.ServerID = response.Data
|
||||
|
||||
return cws.EmptyPacket
|
||||
},
|
||||
)
|
||||
|
||||
oclient.Receive(
|
||||
"initwebrtc",
|
||||
func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
log.Println("Received user SDP")
|
||||
localSession, err := s.peerconnection.StartClient(resp.Data, iceCandidates, config.Width, config.Height)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
log.Println("Error: Cannot create new webrtc session", err)
|
||||
return cws.EmptyPacket
|
||||
}
|
||||
}
|
||||
|
||||
return cws.WSPacket{
|
||||
ID: "sdp",
|
||||
Data: localSession,
|
||||
SessionID: s.ID,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Received start from overlord. This happens when bridging
|
||||
// TODO: refactor
|
||||
//oclient.Receive(
|
||||
//"start",
|
||||
//func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
//log.Println("Received a start request from overlord")
|
||||
//log.Println("Add the connection to current room on the host ", resp.SessionID)
|
||||
|
||||
//peerconnection := oclient.peerconnections[resp.SessionID]
|
||||
//log.Println("start session")
|
||||
|
||||
////room := s.handler.createNewRoom(s.GameName, s.RoomID, s.PlayerIndex)
|
||||
//// Request room from Server if roomID is existed on the server
|
||||
//room := s.handler.getRoom(s.RoomID)
|
||||
//if room == nil {
|
||||
//log.Println("Room not found ", s.RoomID)
|
||||
//return cws.EmptyPacket
|
||||
//}
|
||||
//s.handler.detachPeerConn(s.peerconnection)
|
||||
//room.AddConnectionToRoom(peerconnection, s.PlayerIndex)
|
||||
////roomID, isNewRoom := startSession(peerconnection, resp.Data, resp.RoomID, resp.PlayerIndex)
|
||||
//log.Println("Done, sending back")
|
||||
|
||||
//req.ID = "start"
|
||||
//req.RoomID = room.ID
|
||||
//return req
|
||||
//},
|
||||
//)
|
||||
oclient.Receive(
|
||||
"start",
|
||||
func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
log.Println("Received a start request from overlord")
|
||||
log.Println("Add the connection to current room on the host ", resp.SessionID)
|
||||
s.startGameHandler(resp.Data, resp.RoomID, resp.PlayerIndex)
|
||||
},
|
||||
)
|
||||
// heartbeat to keep pinging overlord. We not ping from server to browser, so we don't call heartbeat in browserClient
|
||||
}
|
||||
|
||||
func getServerIDOfRoom(oc *OverlordClient, roomID string) string {
|
||||
log.Println("Request overlord roomID ", roomID)
|
||||
packet := oc.SyncSend(
|
||||
cws.WSPacket{
|
||||
ID: "getRoom",
|
||||
Data: roomID,
|
||||
},
|
||||
)
|
||||
log.Println("Received roomID from overlord ", packet.Data)
|
||||
|
||||
return packet.Data
|
||||
}
|
||||
|
||||
func (s *Session) startGameHandler(gameName, roomID string, playerIndex int) cws.WSPacket {
|
||||
s.GameName = gameName
|
||||
s.RoomID = roomID
|
||||
s.PlayerIndex = playerIndex
|
||||
|
||||
log.Println("Starting game")
|
||||
// If we are connecting to overlord, request corresponding serverID based on roomID
|
||||
// TODO: check if roomID is in the current server
|
||||
room := s.handler.getRoom(s.RoomID)
|
||||
log.Println("Got Room from local ", room, " ID: ", s.RoomID)
|
||||
// If room is not running
|
||||
if room == nil {
|
||||
// Create new room
|
||||
room = s.handler.createNewRoom(s.GameName, s.RoomID, s.PlayerIndex)
|
||||
// Wait for done signal from room
|
||||
go func() {
|
||||
<-room.Done
|
||||
s.handler.detachRoom(room.ID)
|
||||
}()
|
||||
}
|
||||
|
||||
// Attach peerconnection to room. If PC is already in room, don't detach
|
||||
log.Println("Is PC in room", room.IsPCInRoom(s.peerconnection))
|
||||
if !room.IsPCInRoom(s.peerconnection) {
|
||||
s.handler.detachPeerConn(s.peerconnection)
|
||||
room.AddConnectionToRoom(s.peerconnection, s.PlayerIndex)
|
||||
}
|
||||
s.RoomID = room.ID
|
||||
|
||||
// Register room to overlord if we are connecting to overlord
|
||||
if room != nil && s.OverlordClient != nil {
|
||||
s.OverlordClient.Send(cws.WSPacket{
|
||||
ID: "registerRoom",
|
||||
Data: s.RoomID,
|
||||
}, nil)
|
||||
}
|
||||
req.ID = "start"
|
||||
req.RoomID = s.RoomID
|
||||
req.SessionID = s.ID
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
//func (s *Session) bridgeConnection(serverID string, gameName string, roomID string, playerIndex int) {
|
||||
//log.Println("Bridging connection to other Host ", serverID)
|
||||
//client := s.BrowserClient
|
||||
//// Ask client to init
|
||||
|
||||
//log.Println("Requesting offer to browser", serverID)
|
||||
//resp := client.SyncSend(cws.WSPacket{
|
||||
//ID: "requestOffer",
|
||||
//Data: "",
|
||||
//})
|
||||
|
||||
//// Ask overlord to relay SDP packet to serverID
|
||||
//resp.TargetHostID = serverID
|
||||
//log.Println("Sending offer to overlord to relay message to target host", resp.TargetHostID, "with payload")
|
||||
//remoteTargetSDP := s.OverlordClient.SyncSend(resp)
|
||||
//log.Println("Got back remote host SDP, sending to browser")
|
||||
//// Send back remote SDP of remote server to browser
|
||||
//s.BrowserClient.Send(cws.WSPacket{
|
||||
//ID: "sdp",
|
||||
//Data: remoteTargetSDP.Data,
|
||||
//}, nil)
|
||||
//log.Println("Init session done, start game on target host")
|
||||
|
||||
//s.OverlordClient.SyncSend(cws.WSPacket{
|
||||
//ID: "start",
|
||||
//Data: gameName,
|
||||
//TargetHostID: serverID,
|
||||
//RoomID: roomID,
|
||||
//PlayerIndex: playerIndex,
|
||||
//})
|
||||
//log.Println("Game is started on remote host")
|
||||
//}
|
||||
40
cmd/main.go
40
cmd/main.go
|
|
@ -16,11 +16,7 @@ import (
|
|||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
const (
|
||||
gameboyIndex = "./static/gameboy2.html"
|
||||
debugIndex = "./static/gameboy2.html"
|
||||
gamePath = "games"
|
||||
)
|
||||
const gamePath = "games"
|
||||
|
||||
// Time allowed to write a message to the peer.
|
||||
var upgrader = websocket.Upgrader{}
|
||||
|
|
@ -40,13 +36,24 @@ func initilizeOverlord() {
|
|||
|
||||
log.Println("http://localhost:9000")
|
||||
|
||||
// Can consider Overlord works as server but it is complicated
|
||||
http.HandleFunc("/", overlord.GetWeb)
|
||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
|
||||
|
||||
// browser facing port
|
||||
go func() {
|
||||
http.HandleFunc("/ws", overlord.WS)
|
||||
http.ListenAndServe(":8000", nil)
|
||||
}()
|
||||
|
||||
// worker facing port
|
||||
http.HandleFunc("/wso", overlord.WSO)
|
||||
http.ListenAndServe(":9000", nil)
|
||||
|
||||
log.Println("http://localhost:" + *config.Port)
|
||||
}
|
||||
|
||||
// initializeServer setup a server
|
||||
func initializeServer() {
|
||||
// initializeWorker setup a worker
|
||||
func initializeWorker() {
|
||||
conn, err := createOverlordConnection()
|
||||
if err != nil {
|
||||
log.Println("Cannot connect to overlord")
|
||||
|
|
@ -55,15 +62,13 @@ func initializeServer() {
|
|||
|
||||
handler := handler.NewHandler(conn, *config.IsDebug, gamePath)
|
||||
|
||||
handler.Run()
|
||||
|
||||
// ignore origin
|
||||
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
|
||||
//upgrader.CheckOrigin = func(r *http.Request) bool { return true }
|
||||
|
||||
http.HandleFunc("/", handler.GetWeb)
|
||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
|
||||
http.HandleFunc("/ws", handler.WS)
|
||||
|
||||
log.Println("http://localhost:" + *config.Port)
|
||||
http.ListenAndServe(":"+*config.Port, nil)
|
||||
//http.ListenAndServe(":"+*config.Port, nil)
|
||||
//log.Println("http://localhost:" + *config.Port)
|
||||
}
|
||||
|
||||
func monitor() {
|
||||
|
|
@ -75,7 +80,6 @@ func monitor() {
|
|||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
log.Println("Usage: ./game [-debug]")
|
||||
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
|
||||
|
|
@ -92,7 +96,7 @@ func main() {
|
|||
if strings.HasPrefix(*config.OverlordHost, "ws") && !strings.HasSuffix(*config.OverlordHost, "wso") {
|
||||
log.Fatal("Overlord connection is invalid. Should have the form `ws://.../wso`")
|
||||
}
|
||||
log.Println("Running as slave ")
|
||||
initializeServer()
|
||||
log.Println("Running as worker ")
|
||||
initializeWorker()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
628
cmd/main_test.go
628
cmd/main_test.go
|
|
@ -5,7 +5,6 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -24,17 +23,19 @@ var host = "http://localhost:8000"
|
|||
var testGamePath = "../games"
|
||||
var webrtcconfig = webrtc.Configuration{ICEServers: []webrtc.ICEServer{{URLs: []string{"stun:stun.l.google.com:19302"}}}}
|
||||
|
||||
func initOverlord() *httptest.Server {
|
||||
func initOverlord() (*httptest.Server, *httptest.Server) {
|
||||
server := overlord.NewServer()
|
||||
overlord := httptest.NewServer(http.HandlerFunc(server.WSO))
|
||||
return overlord
|
||||
overlordWorker := httptest.NewServer(http.HandlerFunc(server.WSO))
|
||||
overlordBrowser := httptest.NewServer(http.HandlerFunc(server.WS))
|
||||
return overlordWorker, overlordBrowser
|
||||
}
|
||||
|
||||
func initServer(t *testing.T, oconn *websocket.Conn) *httptest.Server {
|
||||
fmt.Println("Spawn new server")
|
||||
func initWorker(t *testing.T, oconn *websocket.Conn) *handler.Handler {
|
||||
fmt.Println("Spawn new worker")
|
||||
handler := handler.NewHandler(oconn, true, testGamePath)
|
||||
server := httptest.NewServer(http.HandlerFunc(handler.WS))
|
||||
return server
|
||||
go handler.Run()
|
||||
//server := httptest.NewServer(http.HandlerFunc(handler.WS))
|
||||
return handler
|
||||
}
|
||||
|
||||
func connectTestOverlordServer(t *testing.T, overlordURL string) *websocket.Conn {
|
||||
|
|
@ -57,8 +58,8 @@ func initClient(t *testing.T, host string) (client *cws.Client) {
|
|||
// Convert http://127.0.0.1 to ws://127.0.0.
|
||||
u := "ws" + strings.TrimPrefix(host, "http")
|
||||
|
||||
// Connect to the server
|
||||
fmt.Println("Connecting to server")
|
||||
// Connect to the overlord
|
||||
fmt.Println("Connecting to ", u)
|
||||
ws, _, err := websocket.DefaultDialer.Dial(u, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
|
|
@ -82,7 +83,7 @@ func initClient(t *testing.T, host string) (client *cws.Client) {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Send offer to server
|
||||
// Send offer to overlord
|
||||
log.Println("Browser Client")
|
||||
client = cws.NewClient(ws)
|
||||
go client.Listen()
|
||||
|
|
@ -141,42 +142,6 @@ func initClient(t *testing.T, host string) (client *cws.Client) {
|
|||
// If receive roomID, the server is running correctly
|
||||
}
|
||||
|
||||
func TestSingleServerNoOverlord(t *testing.T) {
|
||||
/*
|
||||
Case scenario:
|
||||
- A server X are initilized
|
||||
- Client join room with no coordinator
|
||||
Expected behavior:
|
||||
- Room received not empty
|
||||
*/
|
||||
|
||||
// Init slave server
|
||||
s := initServer(t, nil)
|
||||
defer s.Close()
|
||||
|
||||
client := initClient(t, s.URL)
|
||||
defer client.Close()
|
||||
|
||||
roomID := make(chan string)
|
||||
client.Send(cws.WSPacket{
|
||||
ID: "start",
|
||||
Data: "Contra.nes",
|
||||
RoomID: "",
|
||||
PlayerIndex: 1,
|
||||
}, func(resp cws.WSPacket) {
|
||||
fmt.Println("RoomID:", resp.RoomID)
|
||||
roomID <- resp.RoomID
|
||||
})
|
||||
|
||||
respRoomID := <-roomID
|
||||
if respRoomID == "" {
|
||||
fmt.Println("RoomID should not be empty")
|
||||
t.Fail()
|
||||
}
|
||||
time.Sleep(3 * time.Second)
|
||||
fmt.Println("Done")
|
||||
}
|
||||
|
||||
func TestSingleServerOneOverlord(t *testing.T) {
|
||||
/*
|
||||
Case scenario:
|
||||
|
|
@ -186,16 +151,18 @@ func TestSingleServerOneOverlord(t *testing.T) {
|
|||
- Room received not empty.
|
||||
*/
|
||||
|
||||
o := initOverlord()
|
||||
defer o.Close()
|
||||
oworker, obrowser := initOverlord()
|
||||
defer obrowser.Close()
|
||||
defer oworker.Close()
|
||||
|
||||
oconn := connectTestOverlordServer(t, o.URL)
|
||||
oconn := connectTestOverlordServer(t, oworker.URL)
|
||||
defer oconn.Close()
|
||||
// Init slave server
|
||||
s := initServer(t, oconn)
|
||||
defer s.Close()
|
||||
// Init worker
|
||||
initWorker(t, oconn)
|
||||
//defer h.Close()
|
||||
|
||||
client := initClient(t, s.URL)
|
||||
// connect overlord
|
||||
client := initClient(t, obrowser.URL)
|
||||
defer client.Close()
|
||||
|
||||
fmt.Println("Sending start...")
|
||||
|
|
@ -206,7 +173,6 @@ func TestSingleServerOneOverlord(t *testing.T) {
|
|||
RoomID: "",
|
||||
PlayerIndex: 1,
|
||||
}, func(resp cws.WSPacket) {
|
||||
fmt.Println("RoomID:", resp.RoomID)
|
||||
roomID <- resp.RoomID
|
||||
})
|
||||
|
||||
|
|
@ -219,325 +185,325 @@ func TestSingleServerOneOverlord(t *testing.T) {
|
|||
fmt.Println("Done")
|
||||
}
|
||||
|
||||
func TestTwoServerOneOverlord(t *testing.T) {
|
||||
/*
|
||||
Case scenario:
|
||||
- Two server X, Y are initilized
|
||||
- Client A creates a room on server X
|
||||
- Client B creates a room on server Y
|
||||
- Client B join a room created by A
|
||||
Expected behavior:
|
||||
- Bridge connection will be conducted between server Y and X
|
||||
- Client B can join a room hosted on A
|
||||
*/
|
||||
//func TestTwoServerOneOverlord(t *testing.T) {
|
||||
//[>
|
||||
//Case scenario:
|
||||
//- Two server X, Y are initilized
|
||||
//- Client A creates a room on server X
|
||||
//- Client B creates a room on server Y
|
||||
//- Client B join a room created by A
|
||||
//Expected behavior:
|
||||
//- Bridge connection will be conducted between server Y and X
|
||||
//- Client B can join a room hosted on A
|
||||
//*/
|
||||
|
||||
o := initOverlord()
|
||||
defer o.Close()
|
||||
//o := initOverlord()
|
||||
//defer o.Close()
|
||||
|
||||
oconn1 := connectTestOverlordServer(t, o.URL)
|
||||
// Init slave server
|
||||
s1 := initServer(t, oconn1)
|
||||
defer s1.Close()
|
||||
//oconn1 := connectTestOverlordServer(t, o.URL)
|
||||
//// Init slave server
|
||||
//s1 := initServer(t, oconn1)
|
||||
//defer s1.Close()
|
||||
|
||||
oconn2 := connectTestOverlordServer(t, o.URL)
|
||||
s2 := initServer(t, oconn2)
|
||||
defer s2.Close()
|
||||
//oconn2 := connectTestOverlordServer(t, o.URL)
|
||||
//s2 := initServer(t, oconn2)
|
||||
//defer s2.Close()
|
||||
|
||||
client1 := initClient(t, s1.URL)
|
||||
defer client1.Close()
|
||||
//client1 := initClient(t, s1.URL)
|
||||
//defer client1.Close()
|
||||
|
||||
roomID := make(chan string)
|
||||
client1.Send(cws.WSPacket{
|
||||
ID: "start",
|
||||
Data: "Contra.nes",
|
||||
RoomID: "",
|
||||
PlayerIndex: 1,
|
||||
}, func(resp cws.WSPacket) {
|
||||
fmt.Println("RoomID:", resp.RoomID)
|
||||
roomID <- resp.RoomID
|
||||
})
|
||||
//roomID := make(chan string)
|
||||
//client1.Send(cws.WSPacket{
|
||||
//ID: "start",
|
||||
//Data: "Contra.nes",
|
||||
//RoomID: "",
|
||||
//PlayerIndex: 1,
|
||||
//}, func(resp cws.WSPacket) {
|
||||
//fmt.Println("RoomID:", resp.RoomID)
|
||||
//roomID <- resp.RoomID
|
||||
//})
|
||||
|
||||
remoteRoomID := <-roomID
|
||||
if remoteRoomID == "" {
|
||||
fmt.Println("RoomID should not be empty")
|
||||
t.Fail()
|
||||
}
|
||||
fmt.Println("Done create a room in server 1")
|
||||
//remoteRoomID := <-roomID
|
||||
//if remoteRoomID == "" {
|
||||
//fmt.Println("RoomID should not be empty")
|
||||
//t.Fail()
|
||||
//}
|
||||
//fmt.Println("Done create a room in server 1")
|
||||
|
||||
// ------------------------------------
|
||||
// Client2 trying to create a random room and later join the the room on server1
|
||||
client2 := initClient(t, s2.URL)
|
||||
defer client2.Close()
|
||||
// Wait
|
||||
// Doing the same create local room.
|
||||
localRoomID := make(chan string)
|
||||
client2.Send(cws.WSPacket{
|
||||
ID: "start",
|
||||
Data: "Contra.nes",
|
||||
RoomID: "",
|
||||
PlayerIndex: 1,
|
||||
}, func(resp cws.WSPacket) {
|
||||
fmt.Println("RoomID:", resp.RoomID)
|
||||
localRoomID <- resp.RoomID
|
||||
})
|
||||
//// ------------------------------------
|
||||
//// Client2 trying to create a random room and later join the the room on server1
|
||||
//client2 := initClient(t, s2.URL)
|
||||
//defer client2.Close()
|
||||
//// Wait
|
||||
//// Doing the same create local room.
|
||||
//localRoomID := make(chan string)
|
||||
//client2.Send(cws.WSPacket{
|
||||
//ID: "start",
|
||||
//Data: "Contra.nes",
|
||||
//RoomID: "",
|
||||
//PlayerIndex: 1,
|
||||
//}, func(resp cws.WSPacket) {
|
||||
//fmt.Println("RoomID:", resp.RoomID)
|
||||
//localRoomID <- resp.RoomID
|
||||
//})
|
||||
|
||||
<-localRoomID
|
||||
//<-localRoomID
|
||||
|
||||
fmt.Println("Request the room from server 1", remoteRoomID)
|
||||
log.Println("Server2 trying to join server1 room")
|
||||
// After trying loging in to one session, login to other with the roomID
|
||||
bridgeRoom := make(chan string)
|
||||
client2.Send(cws.WSPacket{
|
||||
ID: "start",
|
||||
Data: "Contra.nes",
|
||||
RoomID: remoteRoomID,
|
||||
PlayerIndex: 1,
|
||||
}, func(resp cws.WSPacket) {
|
||||
fmt.Println("RoomID:", resp.RoomID)
|
||||
bridgeRoom <- resp.RoomID
|
||||
})
|
||||
//fmt.Println("Request the room from server 1", remoteRoomID)
|
||||
//log.Println("Server2 trying to join server1 room")
|
||||
//// After trying loging in to one session, login to other with the roomID
|
||||
//bridgeRoom := make(chan string)
|
||||
//client2.Send(cws.WSPacket{
|
||||
//ID: "start",
|
||||
//Data: "Contra.nes",
|
||||
//RoomID: remoteRoomID,
|
||||
//PlayerIndex: 1,
|
||||
//}, func(resp cws.WSPacket) {
|
||||
//fmt.Println("RoomID:", resp.RoomID)
|
||||
//bridgeRoom <- resp.RoomID
|
||||
//})
|
||||
|
||||
<-bridgeRoom
|
||||
//respRoomID := <-bridgeRoom
|
||||
//if respRoomID == "" {
|
||||
//fmt.Println("The room ID should be equal to the saved room")
|
||||
//t.Fail()
|
||||
//}
|
||||
// If receive roomID, the server is running correctly
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println("Done")
|
||||
}
|
||||
//<-bridgeRoom
|
||||
////respRoomID := <-bridgeRoom
|
||||
////if respRoomID == "" {
|
||||
////fmt.Println("The room ID should be equal to the saved room")
|
||||
////t.Fail()
|
||||
////}
|
||||
//// If receive roomID, the server is running correctly
|
||||
//time.Sleep(time.Second)
|
||||
//fmt.Println("Done")
|
||||
//}
|
||||
|
||||
func TestReconnectRoomNoOverlord(t *testing.T) {
|
||||
/*
|
||||
Case scenario:
|
||||
- A server X is initialized
|
||||
- Client A creates a room K on server X
|
||||
- Server X is turned down, Client is closed
|
||||
- Spawn a new server and a new client connecting to the same room K
|
||||
Expected behavior:
|
||||
- The game should be continue
|
||||
TODO: Current test just make sure the game is running, not check if the game is the same
|
||||
*/
|
||||
//func TestReconnectRoomNoOverlord(t *testing.T) {
|
||||
//[>
|
||||
//Case scenario:
|
||||
//- A server X is initialized
|
||||
//- Client A creates a room K on server X
|
||||
//- Server X is turned down, Client is closed
|
||||
//- Spawn a new server and a new client connecting to the same room K
|
||||
//Expected behavior:
|
||||
//- The game should be continue
|
||||
//TODO: Current test just make sure the game is running, not check if the game is the same
|
||||
//*/
|
||||
|
||||
// Init slave server
|
||||
s := initServer(t, nil)
|
||||
//// Init slave server
|
||||
//s := initServer(t, nil)
|
||||
|
||||
client := initClient(t, s.URL)
|
||||
//client := initClient(t, s.URL)
|
||||
|
||||
fmt.Println("Sending start...")
|
||||
roomID := make(chan string)
|
||||
client.Send(cws.WSPacket{
|
||||
ID: "start",
|
||||
Data: "Contra.nes",
|
||||
RoomID: "",
|
||||
PlayerIndex: 1,
|
||||
}, func(resp cws.WSPacket) {
|
||||
fmt.Println("RoomID:", resp.RoomID)
|
||||
roomID <- resp.RoomID
|
||||
})
|
||||
//fmt.Println("Sending start...")
|
||||
//roomID := make(chan string)
|
||||
//client.Send(cws.WSPacket{
|
||||
//ID: "start",
|
||||
//Data: "Contra.nes",
|
||||
//RoomID: "",
|
||||
//PlayerIndex: 1,
|
||||
//}, func(resp cws.WSPacket) {
|
||||
//fmt.Println("RoomID:", resp.RoomID)
|
||||
//roomID <- resp.RoomID
|
||||
//})
|
||||
|
||||
saveRoomID := <-roomID
|
||||
if saveRoomID == "" {
|
||||
fmt.Println("RoomID should not be empty")
|
||||
t.Fail()
|
||||
}
|
||||
//saveRoomID := <-roomID
|
||||
//if saveRoomID == "" {
|
||||
//fmt.Println("RoomID should not be empty")
|
||||
//t.Fail()
|
||||
//}
|
||||
|
||||
log.Println("Closing room and server")
|
||||
client.Close()
|
||||
s.Close()
|
||||
// Close server and reconnect
|
||||
//log.Println("Closing room and server")
|
||||
//client.Close()
|
||||
//s.Close()
|
||||
//// Close server and reconnect
|
||||
|
||||
// Respawn slave server
|
||||
s = initServer(t, nil)
|
||||
defer s.Close()
|
||||
//// Respawn slave server
|
||||
//s = initServer(t, nil)
|
||||
//defer s.Close()
|
||||
|
||||
client = initClient(t, s.URL)
|
||||
defer client.Close()
|
||||
//client = initClient(t, s.URL)
|
||||
//defer client.Close()
|
||||
|
||||
fmt.Println("Re-access room ", saveRoomID)
|
||||
roomID = make(chan string)
|
||||
client.Send(cws.WSPacket{
|
||||
ID: "start",
|
||||
Data: "Contra.nes",
|
||||
RoomID: saveRoomID,
|
||||
PlayerIndex: 1,
|
||||
}, func(resp cws.WSPacket) {
|
||||
fmt.Println("RoomID:", resp.RoomID)
|
||||
roomID <- resp.RoomID
|
||||
})
|
||||
//fmt.Println("Re-access room ", saveRoomID)
|
||||
//roomID = make(chan string)
|
||||
//client.Send(cws.WSPacket{
|
||||
//ID: "start",
|
||||
//Data: "Contra.nes",
|
||||
//RoomID: saveRoomID,
|
||||
//PlayerIndex: 1,
|
||||
//}, func(resp cws.WSPacket) {
|
||||
//fmt.Println("RoomID:", resp.RoomID)
|
||||
//roomID <- resp.RoomID
|
||||
//})
|
||||
|
||||
respRoomID := <-roomID
|
||||
if respRoomID == "" || respRoomID != saveRoomID {
|
||||
fmt.Println("The room ID should be equal to the saved room")
|
||||
t.Fail()
|
||||
}
|
||||
//respRoomID := <-roomID
|
||||
//if respRoomID == "" || respRoomID != saveRoomID {
|
||||
//fmt.Println("The room ID should be equal to the saved room")
|
||||
//t.Fail()
|
||||
//}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println("Done")
|
||||
//time.Sleep(time.Second)
|
||||
//fmt.Println("Done")
|
||||
|
||||
}
|
||||
//}
|
||||
|
||||
func TestReconnectRoomWithOverlord(t *testing.T) {
|
||||
/*
|
||||
Case scenario:
|
||||
- A server X is initialized connecting to overlord
|
||||
- Client A creates a room K on server X
|
||||
- Server X is turned down, Client is closed
|
||||
- Spawn a new server and a new client connecting to the same room K
|
||||
Expected behavior:
|
||||
- The game should be continue
|
||||
TODO: Current test just make sure the game is running, not check if the game is the same
|
||||
*/
|
||||
//func TestReconnectRoomWithOverlord(t *testing.T) {
|
||||
//[>
|
||||
//Case scenario:
|
||||
//- A server X is initialized connecting to overlord
|
||||
//- Client A creates a room K on server X
|
||||
//- Server X is turned down, Client is closed
|
||||
//- Spawn a new server and a new client connecting to the same room K
|
||||
//Expected behavior:
|
||||
//- The game should be continue
|
||||
//TODO: Current test just make sure the game is running, not check if the game is the same
|
||||
//*/
|
||||
|
||||
o := initOverlord()
|
||||
defer o.Close()
|
||||
//o := initOverlord()
|
||||
//defer o.Close()
|
||||
|
||||
oconn := connectTestOverlordServer(t, o.URL)
|
||||
// Init slave server
|
||||
s := initServer(t, oconn)
|
||||
//oconn := connectTestOverlordServer(t, o.URL)
|
||||
//// Init slave server
|
||||
//s := initServer(t, oconn)
|
||||
|
||||
client := initClient(t, s.URL)
|
||||
//client := initClient(t, s.URL)
|
||||
|
||||
fmt.Println("Sending start...")
|
||||
roomID := make(chan string)
|
||||
client.Send(cws.WSPacket{
|
||||
ID: "start",
|
||||
Data: "Contra.nes",
|
||||
RoomID: "",
|
||||
PlayerIndex: 1,
|
||||
}, func(resp cws.WSPacket) {
|
||||
fmt.Println("RoomID:", resp.RoomID)
|
||||
roomID <- resp.RoomID
|
||||
})
|
||||
//fmt.Println("Sending start...")
|
||||
//roomID := make(chan string)
|
||||
//client.Send(cws.WSPacket{
|
||||
//ID: "start",
|
||||
//Data: "Contra.nes",
|
||||
//RoomID: "",
|
||||
//PlayerIndex: 1,
|
||||
//}, func(resp cws.WSPacket) {
|
||||
//fmt.Println("RoomID:", resp.RoomID)
|
||||
//roomID <- resp.RoomID
|
||||
//})
|
||||
|
||||
saveRoomID := <-roomID
|
||||
if saveRoomID == "" {
|
||||
fmt.Println("RoomID should not be empty")
|
||||
t.Fail()
|
||||
}
|
||||
//saveRoomID := <-roomID
|
||||
//if saveRoomID == "" {
|
||||
//fmt.Println("RoomID should not be empty")
|
||||
//t.Fail()
|
||||
//}
|
||||
|
||||
log.Println("Closing room and server")
|
||||
client.Close()
|
||||
s.Close()
|
||||
oconn.Close()
|
||||
//log.Println("Closing room and server")
|
||||
//client.Close()
|
||||
//s.Close()
|
||||
//oconn.Close()
|
||||
|
||||
// Close server and reconnect
|
||||
//// Close server and reconnect
|
||||
|
||||
log.Println("Server respawn")
|
||||
// Init slave server again
|
||||
oconn = connectTestOverlordServer(t, o.URL)
|
||||
defer oconn.Close()
|
||||
s = initServer(t, oconn)
|
||||
defer s.Close()
|
||||
//log.Println("Server respawn")
|
||||
//// Init slave server again
|
||||
//oconn = connectTestOverlordServer(t, o.URL)
|
||||
//defer oconn.Close()
|
||||
//s = initServer(t, oconn)
|
||||
//defer s.Close()
|
||||
|
||||
client = initClient(t, s.URL)
|
||||
defer client.Close()
|
||||
//client = initClient(t, s.URL)
|
||||
//defer client.Close()
|
||||
|
||||
fmt.Println("Re-access room ", saveRoomID)
|
||||
roomID = make(chan string)
|
||||
client.Send(cws.WSPacket{
|
||||
ID: "start",
|
||||
Data: "Contra.nes",
|
||||
RoomID: saveRoomID,
|
||||
PlayerIndex: 1,
|
||||
}, func(resp cws.WSPacket) {
|
||||
fmt.Println("RoomID:", resp.RoomID)
|
||||
roomID <- resp.RoomID
|
||||
})
|
||||
//fmt.Println("Re-access room ", saveRoomID)
|
||||
//roomID = make(chan string)
|
||||
//client.Send(cws.WSPacket{
|
||||
//ID: "start",
|
||||
//Data: "Contra.nes",
|
||||
//RoomID: saveRoomID,
|
||||
//PlayerIndex: 1,
|
||||
//}, func(resp cws.WSPacket) {
|
||||
//fmt.Println("RoomID:", resp.RoomID)
|
||||
//roomID <- resp.RoomID
|
||||
//})
|
||||
|
||||
respRoomID := <-roomID
|
||||
if respRoomID == "" || respRoomID != saveRoomID {
|
||||
fmt.Println("The room ID should be equal to the saved room")
|
||||
t.Fail()
|
||||
}
|
||||
//respRoomID := <-roomID
|
||||
//if respRoomID == "" || respRoomID != saveRoomID {
|
||||
//fmt.Println("The room ID should be equal to the saved room")
|
||||
//t.Fail()
|
||||
//}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println("Done")
|
||||
}
|
||||
//time.Sleep(time.Second)
|
||||
//fmt.Println("Done")
|
||||
//}
|
||||
|
||||
func TestRejoinNoOverlordMultiple(t *testing.T) {
|
||||
/*
|
||||
Case scenario:
|
||||
- A server X without connecting to overlord
|
||||
- Client A keeps creating a new room
|
||||
Expected behavior:
|
||||
- The game should running normally
|
||||
*/
|
||||
//func TestRejoinNoOverlordMultiple(t *testing.T) {
|
||||
//[>
|
||||
//Case scenario:
|
||||
//- A server X without connecting to overlord
|
||||
//- Client A keeps creating a new room
|
||||
//Expected behavior:
|
||||
//- The game should running normally
|
||||
//*/
|
||||
|
||||
// Init slave server
|
||||
s := initServer(t, nil)
|
||||
defer s.Close()
|
||||
//// Init slave server
|
||||
//s := initServer(t, nil)
|
||||
//defer s.Close()
|
||||
|
||||
fmt.Println("Num goRoutine before start: ", runtime.NumGoroutine())
|
||||
client := initClient(t, s.URL)
|
||||
for i := 0; i < 100; i++ {
|
||||
fmt.Println("Sending start...")
|
||||
// Keep starting game
|
||||
roomID := make(chan string)
|
||||
client.Send(cws.WSPacket{
|
||||
ID: "start",
|
||||
Data: "Contra.nes",
|
||||
RoomID: "",
|
||||
PlayerIndex: 1,
|
||||
}, func(resp cws.WSPacket) {
|
||||
fmt.Println("RoomID:", resp.RoomID)
|
||||
roomID <- resp.RoomID
|
||||
})
|
||||
//fmt.Println("Num goRoutine before start: ", runtime.NumGoroutine())
|
||||
//client := initClient(t, s.URL)
|
||||
//for i := 0; i < 100; i++ {
|
||||
//fmt.Println("Sending start...")
|
||||
//// Keep starting game
|
||||
//roomID := make(chan string)
|
||||
//client.Send(cws.WSPacket{
|
||||
//ID: "start",
|
||||
//Data: "Contra.nes",
|
||||
//RoomID: "",
|
||||
//PlayerIndex: 1,
|
||||
//}, func(resp cws.WSPacket) {
|
||||
//fmt.Println("RoomID:", resp.RoomID)
|
||||
//roomID <- resp.RoomID
|
||||
//})
|
||||
|
||||
respRoomID := <-roomID
|
||||
if respRoomID == "" {
|
||||
fmt.Println("The room ID should be equal to the saved room")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println("Num goRoutine should be small: ", runtime.NumGoroutine())
|
||||
fmt.Println("Done")
|
||||
//respRoomID := <-roomID
|
||||
//if respRoomID == "" {
|
||||
//fmt.Println("The room ID should be equal to the saved room")
|
||||
//t.Fail()
|
||||
//}
|
||||
//}
|
||||
//time.Sleep(time.Second)
|
||||
//fmt.Println("Num goRoutine should be small: ", runtime.NumGoroutine())
|
||||
//fmt.Println("Done")
|
||||
|
||||
}
|
||||
//}
|
||||
|
||||
func TestRejoinWithOverlordMultiple(t *testing.T) {
|
||||
/*
|
||||
Case scenario:
|
||||
- A server X is initialized connecting to overlord
|
||||
- Client A keeps creating a new room
|
||||
Expected behavior:
|
||||
- The game should running normally
|
||||
*/
|
||||
//func TestRejoinWithOverlordMultiple(t *testing.T) {
|
||||
//[>
|
||||
//Case scenario:
|
||||
//- A server X is initialized connecting to overlord
|
||||
//- Client A keeps creating a new room
|
||||
//Expected behavior:
|
||||
//- The game should running normally
|
||||
//*/
|
||||
|
||||
// Init slave server
|
||||
o := initOverlord()
|
||||
defer o.Close()
|
||||
//// Init slave server
|
||||
//o := initOverlord()
|
||||
//defer o.Close()
|
||||
|
||||
oconn := connectTestOverlordServer(t, o.URL)
|
||||
// Init slave server
|
||||
s := initServer(t, oconn)
|
||||
defer s.Close()
|
||||
//oconn := connectTestOverlordServer(t, o.URL)
|
||||
//// Init slave server
|
||||
//s := initServer(t, oconn)
|
||||
//defer s.Close()
|
||||
|
||||
fmt.Println("Num goRoutine before start: ", runtime.NumGoroutine())
|
||||
client := initClient(t, s.URL)
|
||||
for i := 0; i < 100; i++ {
|
||||
fmt.Println("Sending start...")
|
||||
// Keep starting game
|
||||
roomID := make(chan string)
|
||||
client.Send(cws.WSPacket{
|
||||
ID: "start",
|
||||
Data: "Contra.nes",
|
||||
RoomID: "",
|
||||
PlayerIndex: 1,
|
||||
}, func(resp cws.WSPacket) {
|
||||
fmt.Println("RoomID:", resp.RoomID)
|
||||
roomID <- resp.RoomID
|
||||
})
|
||||
//fmt.Println("Num goRoutine before start: ", runtime.NumGoroutine())
|
||||
//client := initClient(t, s.URL)
|
||||
//for i := 0; i < 100; i++ {
|
||||
//fmt.Println("Sending start...")
|
||||
//// Keep starting game
|
||||
//roomID := make(chan string)
|
||||
//client.Send(cws.WSPacket{
|
||||
//ID: "start",
|
||||
//Data: "Contra.nes",
|
||||
//RoomID: "",
|
||||
//PlayerIndex: 1,
|
||||
//}, func(resp cws.WSPacket) {
|
||||
//fmt.Println("RoomID:", resp.RoomID)
|
||||
//roomID <- resp.RoomID
|
||||
//})
|
||||
|
||||
respRoomID := <-roomID
|
||||
if respRoomID == "" {
|
||||
fmt.Println("The room ID should be equal to the saved room")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
fmt.Println("Num goRoutine should be small: ", runtime.NumGoroutine())
|
||||
fmt.Println("Done")
|
||||
//respRoomID := <-roomID
|
||||
//if respRoomID == "" {
|
||||
//fmt.Println("The room ID should be equal to the saved room")
|
||||
//t.Fail()
|
||||
//}
|
||||
//}
|
||||
//fmt.Println("Num goRoutine should be small: ", runtime.NumGoroutine())
|
||||
//fmt.Println("Done")
|
||||
|
||||
}
|
||||
//}
|
||||
|
|
|
|||
20
cws/cws.go
20
cws/cws.go
|
|
@ -68,11 +68,11 @@ func (c *Client) Send(request WSPacket, callback func(response WSPacket)) {
|
|||
// Wrap callback with sessionID and packetID
|
||||
if callback != nil {
|
||||
wrapperCallback := func(resp WSPacket) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Println("Recovered from err", err)
|
||||
}
|
||||
}()
|
||||
//defer func() {
|
||||
//if err := recover(); err != nil {
|
||||
//log.Println("Recovered from err", err)
|
||||
//}
|
||||
//}()
|
||||
|
||||
resp.PacketID = request.PacketID
|
||||
resp.SessionID = request.SessionID
|
||||
|
|
@ -91,11 +91,11 @@ func (c *Client) Send(request WSPacket, callback func(response WSPacket)) {
|
|||
// Receive receive and response back
|
||||
func (c *Client) Receive(id string, f func(response WSPacket) (request WSPacket)) {
|
||||
c.recvCallback[id] = func(response WSPacket) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Println("Recovered from err", err)
|
||||
}
|
||||
}()
|
||||
//defer func() {
|
||||
//if err := recover(); err != nil {
|
||||
//log.Println("Recovered from err", err)
|
||||
//}
|
||||
//}()
|
||||
|
||||
req := f(response)
|
||||
// Add Meta data
|
||||
|
|
|
|||
|
|
@ -1,164 +0,0 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/giongto35/cloud-game/config"
|
||||
"github.com/giongto35/cloud-game/cws"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type BrowserClient struct {
|
||||
*cws.Client
|
||||
}
|
||||
|
||||
// RouteBrowser are all routes server received from browser
|
||||
func (s *Session) RouteBrowser() {
|
||||
iceCandidates := [][]byte{}
|
||||
|
||||
browserClient := s.BrowserClient
|
||||
|
||||
browserClient.Receive("heartbeat", func(resp cws.WSPacket) cws.WSPacket {
|
||||
return resp
|
||||
})
|
||||
|
||||
browserClient.Receive("icecandidate", func(resp cws.WSPacket) cws.WSPacket {
|
||||
log.Println("Received candidates ", resp.Data)
|
||||
iceCandidates = append(iceCandidates, []byte(resp.Data))
|
||||
return cws.EmptyPacket
|
||||
})
|
||||
|
||||
browserClient.Receive("initwebrtc", func(resp cws.WSPacket) cws.WSPacket {
|
||||
log.Println("Received user SDP")
|
||||
localSession, err := s.peerconnection.StartClient(resp.Data, iceCandidates, config.Width, config.Height)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
log.Println("Error: Cannot create new webrtc session", err)
|
||||
return cws.EmptyPacket
|
||||
}
|
||||
}
|
||||
|
||||
return cws.WSPacket{
|
||||
ID: "sdp",
|
||||
Data: localSession,
|
||||
SessionID: s.ID,
|
||||
}
|
||||
})
|
||||
|
||||
// TODO: Add save and load
|
||||
browserClient.Receive("save", func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
log.Println("Saving game state")
|
||||
req.ID = "save"
|
||||
req.Data = "ok"
|
||||
if s.RoomID != "" {
|
||||
room := s.handler.getRoom(s.RoomID)
|
||||
if room == nil {
|
||||
return
|
||||
}
|
||||
err := room.SaveGame()
|
||||
if err != nil {
|
||||
log.Println("[!] Cannot save game state: ", err)
|
||||
req.Data = "error"
|
||||
}
|
||||
} else {
|
||||
req.Data = "error"
|
||||
}
|
||||
|
||||
return req
|
||||
})
|
||||
|
||||
browserClient.Receive("load", func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
log.Println("Loading game state")
|
||||
req.ID = "load"
|
||||
req.Data = "ok"
|
||||
if s.RoomID != "" {
|
||||
room := s.handler.getRoom(s.RoomID)
|
||||
err := room.LoadGame()
|
||||
if err != nil {
|
||||
log.Println("[!] Cannot load game state: ", err)
|
||||
req.Data = "error"
|
||||
}
|
||||
} else {
|
||||
req.Data = "error"
|
||||
}
|
||||
|
||||
return req
|
||||
})
|
||||
|
||||
browserClient.Receive("quit", func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
log.Println("Received quit", req)
|
||||
s.GameName = resp.Data
|
||||
s.RoomID = resp.RoomID
|
||||
s.PlayerIndex = resp.PlayerIndex
|
||||
|
||||
room := s.handler.getRoom(s.RoomID)
|
||||
if room.IsPCInRoom(s.peerconnection) {
|
||||
s.handler.detachPeerConn(s.peerconnection)
|
||||
}
|
||||
|
||||
return cws.EmptyPacket
|
||||
})
|
||||
|
||||
browserClient.Receive("start", func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
s.GameName = resp.Data
|
||||
s.RoomID = resp.RoomID
|
||||
s.PlayerIndex = resp.PlayerIndex
|
||||
|
||||
log.Println("Starting game")
|
||||
// If we are connecting to overlord, request corresponding serverID based on roomID
|
||||
if s.OverlordClient != nil {
|
||||
roomServerID := getServerIDOfRoom(s.OverlordClient, s.RoomID)
|
||||
log.Println("Server of RoomID ", s.RoomID, " is ", roomServerID, " while current server is ", s.ServerID)
|
||||
// If the target serverID is different from current serverID
|
||||
if roomServerID != "" && s.ServerID != roomServerID {
|
||||
// TODO: Re -register
|
||||
// Bridge Connection to the target serverID
|
||||
go s.bridgeConnection(roomServerID, s.GameName, s.RoomID, s.PlayerIndex)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Get Room in local server
|
||||
// TODO: check if roomID is in the current server
|
||||
room := s.handler.getRoom(s.RoomID)
|
||||
log.Println("Got Room from local ", room, " ID: ", s.RoomID)
|
||||
// If room is not running
|
||||
if room == nil {
|
||||
// Create new room
|
||||
room = s.handler.createNewRoom(s.GameName, s.RoomID, s.PlayerIndex)
|
||||
// Wait for done signal from room
|
||||
go func() {
|
||||
<-room.Done
|
||||
s.handler.detachRoom(room.ID)
|
||||
}()
|
||||
}
|
||||
|
||||
// Attach peerconnection to room. If PC is already in room, don't detach
|
||||
log.Println("Is PC in room", room.IsPCInRoom(s.peerconnection))
|
||||
if !room.IsPCInRoom(s.peerconnection) {
|
||||
s.handler.detachPeerConn(s.peerconnection)
|
||||
room.AddConnectionToRoom(s.peerconnection, s.PlayerIndex)
|
||||
}
|
||||
s.RoomID = room.ID
|
||||
|
||||
// Register room to overlord if we are connecting to overlord
|
||||
if room != nil && s.OverlordClient != nil {
|
||||
s.OverlordClient.Send(cws.WSPacket{
|
||||
ID: "registerRoom",
|
||||
Data: s.RoomID,
|
||||
}, nil)
|
||||
}
|
||||
req.ID = "start"
|
||||
req.RoomID = s.RoomID
|
||||
req.SessionID = s.ID
|
||||
|
||||
return req
|
||||
})
|
||||
}
|
||||
|
||||
// NewOverlordClient returns a client connecting to browser. This connection exchanges information between clients and server
|
||||
func NewBrowserClient(c *websocket.Conn) *BrowserClient {
|
||||
return &BrowserClient{
|
||||
Client: cws.NewClient(c),
|
||||
}
|
||||
}
|
||||
|
|
@ -5,13 +5,10 @@ import (
|
|||
"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 (
|
||||
|
|
@ -30,7 +27,7 @@ type Handler struct {
|
|||
// ID of the current server globalwise
|
||||
serverID string
|
||||
// isDebug determines the mode handler is running
|
||||
isDebug bool
|
||||
//isDebug bool
|
||||
// Path to game list
|
||||
gamePath string
|
||||
// All webrtc peerconnections are handled by the server
|
||||
|
|
@ -44,26 +41,34 @@ type Handler struct {
|
|||
func NewHandler(overlordConn *websocket.Conn, isDebug bool, gamePath string) *Handler {
|
||||
onlineStorage := storage.NewInitClient()
|
||||
|
||||
oClient := NewOverlordClient(overlordConn)
|
||||
return &Handler{
|
||||
oClient: NewOverlordClient(overlordConn),
|
||||
oClient: oClient,
|
||||
rooms: map[string]*room.Room{},
|
||||
peerconnections: map[string]*webrtc.WebRTC{},
|
||||
|
||||
isDebug: isDebug,
|
||||
//isDebug: isDebug,
|
||||
gamePath: gamePath,
|
||||
|
||||
onlineStorage: onlineStorage,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) Run() {
|
||||
go h.oClient.Heartbeat()
|
||||
|
||||
h.RouteOverlord()
|
||||
h.oClient.Listen()
|
||||
}
|
||||
|
||||
// GetWeb returns web frontend
|
||||
func (h *Handler) GetWeb(w http.ResponseWriter, r *http.Request) {
|
||||
indexFN := ""
|
||||
if h.isDebug {
|
||||
indexFN = debugIndex
|
||||
} else {
|
||||
indexFN = gameboyIndex
|
||||
}
|
||||
//if h.isDebug {
|
||||
//indexFN = debugIndex
|
||||
//} else {
|
||||
indexFN = gameboyIndex
|
||||
//}
|
||||
|
||||
bs, err := ioutil.ReadFile(indexFN)
|
||||
if err != nil {
|
||||
|
|
@ -72,56 +77,6 @@ func (h *Handler) GetWeb(w http.ResponseWriter, r *http.Request) {
|
|||
w.Write(bs)
|
||||
}
|
||||
|
||||
// WS handles normal traffic (from browser to host)
|
||||
func (h *Handler) WS(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Println("Warn: Something wrong. Recovered in f", r)
|
||||
}
|
||||
}()
|
||||
|
||||
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)
|
||||
|
||||
// If peerconnection is done (client.Done is signalled), we close peerconnection
|
||||
go func() {
|
||||
<-client.Done
|
||||
log.Println("Socket terminated, detach connection")
|
||||
h.detachPeerConn(wssession.peerconnection)
|
||||
}()
|
||||
|
||||
wssession.BrowserClient.Listen()
|
||||
}
|
||||
|
||||
// Detach peerconnection detach/remove a peerconnection from current room
|
||||
func (h *Handler) detachPeerConn(pc *webrtc.WebRTC) {
|
||||
log.Println("Detach peerconnection")
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import (
|
|||
// We expect only one OverlordClient for each server
|
||||
type OverlordClient struct {
|
||||
*cws.Client
|
||||
peerconnections map[string]*webrtc.WebRTC
|
||||
}
|
||||
|
||||
// NewOverlordClient returns a client connecting to overlord for coordiation between different server
|
||||
|
|
@ -23,15 +22,15 @@ func NewOverlordClient(oc *websocket.Conn) *OverlordClient {
|
|||
}
|
||||
|
||||
oclient := &OverlordClient{
|
||||
Client: cws.NewClient(oc),
|
||||
peerconnections: map[string]*webrtc.WebRTC{},
|
||||
Client: cws.NewClient(oc),
|
||||
}
|
||||
return oclient
|
||||
}
|
||||
|
||||
// RouteOverlord are all routes server received from overlord
|
||||
func (s *Session) RouteOverlord() {
|
||||
oclient := s.OverlordClient
|
||||
func (h *Handler) RouteOverlord() {
|
||||
iceCandidates := [][]byte{}
|
||||
oclient := h.oClient
|
||||
|
||||
// Received from overlord the serverID
|
||||
oclient.Receive(
|
||||
|
|
@ -39,62 +38,76 @@ func (s *Session) RouteOverlord() {
|
|||
func(response cws.WSPacket) (request cws.WSPacket) {
|
||||
// Stick session with serverID got from overlord
|
||||
log.Println("Received serverID ", response.Data)
|
||||
s.ServerID = response.Data
|
||||
h.serverID = response.Data
|
||||
|
||||
return cws.EmptyPacket
|
||||
},
|
||||
)
|
||||
|
||||
// Received from overlord the sdp. This is happens when bridging
|
||||
// TODO: refactor
|
||||
oclient.Receive(
|
||||
"initwebrtc",
|
||||
func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
log.Println("Received a sdp request from overlord")
|
||||
log.Println("Start peerconnection from the sdp")
|
||||
log.Println("Received relay SDP of a browser from overlord")
|
||||
peerconnection := webrtc.NewWebRTC()
|
||||
// init new peerconnection from sessionID
|
||||
localSession, err := peerconnection.StartClient(resp.Data, nil, config.Width, config.Height)
|
||||
oclient.peerconnections[resp.SessionID] = peerconnection
|
||||
localSession, err := peerconnection.StartClient(resp.Data, iceCandidates, config.Width, config.Height)
|
||||
h.peerconnections[resp.SessionID] = peerconnection
|
||||
|
||||
log.Println("Start peerconnection")
|
||||
if err != nil {
|
||||
log.Println("Error: Cannot create new webrtc session", err)
|
||||
return cws.EmptyPacket
|
||||
if err != nil {
|
||||
log.Println("Error: Cannot create new webrtc session", err)
|
||||
return cws.EmptyPacket
|
||||
}
|
||||
}
|
||||
|
||||
return cws.WSPacket{
|
||||
ID: "sdp",
|
||||
Data: localSession,
|
||||
ID: "sdp",
|
||||
Data: localSession,
|
||||
SessionID: resp.SessionID,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Received start from overlord. This happens when bridging
|
||||
// TODO: refactor
|
||||
//oclient.Receive(
|
||||
//"start",
|
||||
//func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
//log.Println("Received a start request from overlord")
|
||||
//log.Println("Add the connection to current room on the host ", resp.SessionID)
|
||||
|
||||
//peerconnection := oclient.peerconnections[resp.SessionID]
|
||||
//log.Println("start session")
|
||||
|
||||
////room := s.handler.createNewRoom(s.GameName, s.RoomID, s.PlayerIndex)
|
||||
//// Request room from Server if roomID is existed on the server
|
||||
//room := s.handler.getRoom(s.RoomID)
|
||||
//if room == nil {
|
||||
//log.Println("Room not found ", s.RoomID)
|
||||
//return cws.EmptyPacket
|
||||
//}
|
||||
//s.handler.detachPeerConn(s.peerconnection)
|
||||
//room.AddConnectionToRoom(peerconnection, s.PlayerIndex)
|
||||
////roomID, isNewRoom := startSession(peerconnection, resp.Data, resp.RoomID, resp.PlayerIndex)
|
||||
//log.Println("Done, sending back")
|
||||
|
||||
//req.ID = "start"
|
||||
//req.RoomID = room.ID
|
||||
//return req
|
||||
//},
|
||||
//)
|
||||
|
||||
oclient.Receive(
|
||||
"start",
|
||||
func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
log.Println("Received a start request from overlord")
|
||||
log.Println("Add the connection to current room on the host ", resp.SessionID)
|
||||
|
||||
peerconnection := oclient.peerconnections[resp.SessionID]
|
||||
log.Println("start session")
|
||||
|
||||
//room := s.handler.createNewRoom(s.GameName, s.RoomID, s.PlayerIndex)
|
||||
// Request room from Server if roomID is existed on the server
|
||||
room := s.handler.getRoom(s.RoomID)
|
||||
if room == nil {
|
||||
log.Println("Room not found ", s.RoomID)
|
||||
return cws.EmptyPacket
|
||||
peerconnection := h.peerconnections[resp.SessionID]
|
||||
roomID := h.startGameHandler(resp.Data, resp.RoomID, resp.PlayerIndex, peerconnection)
|
||||
return cws.WSPacket{
|
||||
ID: "start",
|
||||
RoomID: roomID,
|
||||
}
|
||||
s.handler.detachPeerConn(s.peerconnection)
|
||||
room.AddConnectionToRoom(peerconnection, s.PlayerIndex)
|
||||
//roomID, isNewRoom := startSession(peerconnection, resp.Data, resp.RoomID, resp.PlayerIndex)
|
||||
log.Println("Done, sending back")
|
||||
|
||||
req.ID = "start"
|
||||
req.RoomID = room.ID
|
||||
return req
|
||||
},
|
||||
)
|
||||
// heartbeat to keep pinging overlord. We not ping from server to browser, so we don't call heartbeat in browserClient
|
||||
|
|
@ -113,35 +126,74 @@ func getServerIDOfRoom(oc *OverlordClient, roomID string) string {
|
|||
return packet.Data
|
||||
}
|
||||
|
||||
func (s *Session) bridgeConnection(serverID string, gameName string, roomID string, playerIndex int) {
|
||||
log.Println("Bridging connection to other Host ", serverID)
|
||||
client := s.BrowserClient
|
||||
// Ask client to init
|
||||
func (h *Handler) startGameHandler(gameName, roomID string, playerIndex int, peerconnection *webrtc.WebRTC) string {
|
||||
//s.GameName = gameName
|
||||
//s.RoomID = roomID
|
||||
//s.PlayerIndex = playerIndex
|
||||
|
||||
log.Println("Requesting offer to browser", serverID)
|
||||
resp := client.SyncSend(cws.WSPacket{
|
||||
ID: "requestOffer",
|
||||
Data: "",
|
||||
})
|
||||
log.Println("Starting game")
|
||||
// If we are connecting to overlord, request corresponding serverID based on roomID
|
||||
// TODO: check if roomID is in the current server
|
||||
room := h.getRoom(roomID)
|
||||
log.Println("Got Room from local ", room, " ID: ", roomID)
|
||||
// If room is not running
|
||||
if room == nil {
|
||||
// Create new room
|
||||
room = h.createNewRoom(gameName, roomID, playerIndex)
|
||||
// Wait for done signal from room
|
||||
go func() {
|
||||
<-room.Done
|
||||
h.detachRoom(room.ID)
|
||||
}()
|
||||
}
|
||||
|
||||
// Ask overlord to relay SDP packet to serverID
|
||||
resp.TargetHostID = serverID
|
||||
log.Println("Sending offer to overlord to relay message to target host", resp.TargetHostID, "with payload")
|
||||
remoteTargetSDP := s.OverlordClient.SyncSend(resp)
|
||||
log.Println("Got back remote host SDP, sending to browser")
|
||||
// Send back remote SDP of remote server to browser
|
||||
s.BrowserClient.Send(cws.WSPacket{
|
||||
ID: "sdp",
|
||||
Data: remoteTargetSDP.Data,
|
||||
}, nil)
|
||||
log.Println("Init session done, start game on target host")
|
||||
// Attach peerconnection to room. If PC is already in room, don't detach
|
||||
log.Println("Is PC in room", room.IsPCInRoom(peerconnection))
|
||||
if !room.IsPCInRoom(peerconnection) {
|
||||
h.detachPeerConn(peerconnection)
|
||||
room.AddConnectionToRoom(peerconnection, playerIndex)
|
||||
}
|
||||
|
||||
s.OverlordClient.SyncSend(cws.WSPacket{
|
||||
ID: "start",
|
||||
Data: gameName,
|
||||
TargetHostID: serverID,
|
||||
RoomID: roomID,
|
||||
PlayerIndex: playerIndex,
|
||||
})
|
||||
log.Println("Game is started on remote host")
|
||||
// Register room to overlord if we are connecting to overlord
|
||||
if room != nil && h.oClient != nil {
|
||||
h.oClient.Send(cws.WSPacket{
|
||||
ID: "registerRoom",
|
||||
Data: roomID,
|
||||
}, nil)
|
||||
}
|
||||
|
||||
return room.ID
|
||||
}
|
||||
|
||||
//func (s *Session) bridgeConnection(serverID string, gameName string, roomID string, playerIndex int) {
|
||||
//log.Println("Bridging connection to other Host ", serverID)
|
||||
//client := s.BrowserClient
|
||||
//// Ask client to init
|
||||
|
||||
//log.Println("Requesting offer to browser", serverID)
|
||||
//resp := client.SyncSend(cws.WSPacket{
|
||||
//ID: "requestOffer",
|
||||
//Data: "",
|
||||
//})
|
||||
|
||||
//// Ask overlord to relay SDP packet to serverID
|
||||
//resp.TargetHostID = serverID
|
||||
//log.Println("Sending offer to overlord to relay message to target host", resp.TargetHostID, "with payload")
|
||||
//remoteTargetSDP := s.OverlordClient.SyncSend(resp)
|
||||
//log.Println("Got back remote host SDP, sending to browser")
|
||||
//// Send back remote SDP of remote server to browser
|
||||
//s.BrowserClient.Send(cws.WSPacket{
|
||||
//ID: "sdp",
|
||||
//Data: remoteTargetSDP.Data,
|
||||
//}, nil)
|
||||
//log.Println("Init session done, start game on target host")
|
||||
|
||||
//s.OverlordClient.SyncSend(cws.WSPacket{
|
||||
//ID: "start",
|
||||
//Data: gameName,
|
||||
//TargetHostID: serverID,
|
||||
//RoomID: roomID,
|
||||
//PlayerIndex: playerIndex,
|
||||
//})
|
||||
//log.Println("Game is started on remote host")
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"github.com/giongto35/cloud-game/webrtc"
|
||||
)
|
||||
import "github.com/giongto35/cloud-game/webrtc"
|
||||
|
||||
// Session represents a session connected from the browser to the current server
|
||||
// It requires one connection to browser and one connection to the overlord
|
||||
// connection to browser is 1-1. connection to overlord is n - 1
|
||||
// Peerconnection can be from other server to ensure better latency
|
||||
type Session struct {
|
||||
ID string
|
||||
BrowserClient *BrowserClient
|
||||
ID string
|
||||
//BrowserClient *BrowserClient
|
||||
OverlordClient *OverlordClient
|
||||
peerconnection *webrtc.WebRTC
|
||||
|
||||
|
|
@ -22,7 +20,3 @@ type Session struct {
|
|||
RoomID string
|
||||
PlayerIndex int
|
||||
}
|
||||
|
||||
func (s *Session) Close() {
|
||||
s.peerconnection.StopClient()
|
||||
}
|
||||
|
|
|
|||
195
overlord/browser.go
Normal file
195
overlord/browser.go
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
package overlord
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/giongto35/cloud-game/cws"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type BrowserClient struct {
|
||||
*cws.Client
|
||||
}
|
||||
|
||||
// RouteBrowser are all routes server received from browser
|
||||
func (s *Session) RouteBrowser() {
|
||||
iceCandidates := [][]byte{}
|
||||
|
||||
browserClient := s.BrowserClient
|
||||
|
||||
browserClient.Receive("heartbeat", func(resp cws.WSPacket) cws.WSPacket {
|
||||
return resp
|
||||
})
|
||||
|
||||
browserClient.Receive("icecandidate", func(resp cws.WSPacket) cws.WSPacket {
|
||||
log.Println("Received candidates ", resp.Data)
|
||||
iceCandidates = append(iceCandidates, []byte(resp.Data))
|
||||
return cws.EmptyPacket
|
||||
})
|
||||
|
||||
browserClient.Receive("initwebrtc", func(resp cws.WSPacket) cws.WSPacket {
|
||||
log.Println("Overlord: Received a sdp request from a browser")
|
||||
log.Println("Overlord: Relay sdp request from a browser to worker")
|
||||
// relay SDP to target worker and get back SDP of the worker
|
||||
// TODO: Async
|
||||
log.Println("Overlord: serverID: ", s.ServerID)
|
||||
sdp := s.handler.servers[s.ServerID].SyncSend(
|
||||
resp,
|
||||
)
|
||||
|
||||
return sdp
|
||||
})
|
||||
|
||||
browserClient.Receive("quit", func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
log.Println("Received quit", req)
|
||||
s.GameName = resp.Data
|
||||
s.RoomID = resp.RoomID
|
||||
s.PlayerIndex = resp.PlayerIndex
|
||||
|
||||
// TODO:
|
||||
//room := s.handler.getRoom(s.RoomID)
|
||||
//if room.IsPCInRoom(s.peerconnection) {
|
||||
//s.handler.detachPeerConn(s.peerconnection)
|
||||
//}
|
||||
log.Println("Sending to target host", resp.TargetHostID, " ", resp)
|
||||
resp = s.handler.servers[resp.TargetHostID].SyncSend(
|
||||
resp,
|
||||
)
|
||||
|
||||
return cws.EmptyPacket
|
||||
})
|
||||
|
||||
browserClient.Receive("start", func(resp cws.WSPacket) cws.WSPacket {
|
||||
log.Println("Overlord: Received a relay start request from a host")
|
||||
// TODO: Abstract
|
||||
// TODO: if resp.TargetHostID != ""c:w {
|
||||
// relay start to target host
|
||||
log.Println("Sending to target host", resp.TargetHostID, " ", resp)
|
||||
// TODO: Async
|
||||
resp = s.handler.servers[s.ServerID].SyncSend(
|
||||
resp,
|
||||
)
|
||||
|
||||
return resp
|
||||
})
|
||||
|
||||
//browserClient.Receive("initwebrtc", func(resp cws.WSPacket) cws.WSPacket {
|
||||
//log.Println("Received user SDP")
|
||||
//localSession, err := s.peerconnection.StartClient(resp.Data, iceCandidates, config.Width, config.Height)
|
||||
//if err != nil {
|
||||
//if err != nil {
|
||||
//log.Println("Error: Cannot create new webrtc session", err)
|
||||
//return cws.EmptyPacket
|
||||
//}
|
||||
//}
|
||||
|
||||
//return cws.WSPacket{
|
||||
//ID: "sdp",
|
||||
//Data: localSession,
|
||||
//SessionID: s.ID,
|
||||
//}
|
||||
//})
|
||||
|
||||
// TODO: Add save and load
|
||||
//browserClient.Receive("save", func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
//log.Println("Saving game state")
|
||||
//req.ID = "save"
|
||||
//req.Data = "ok"
|
||||
//if s.RoomID != "" {
|
||||
//room := s.handler.getRoom(s.RoomID)
|
||||
//if room == nil {
|
||||
//return
|
||||
//}
|
||||
//err := room.SaveGame()
|
||||
//if err != nil {
|
||||
//log.Println("[!] Cannot save game state: ", err)
|
||||
//req.Data = "error"
|
||||
//}
|
||||
//} else {
|
||||
//req.Data = "error"
|
||||
//}
|
||||
|
||||
//return req
|
||||
//})
|
||||
|
||||
//browserClient.Receive("load", func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
//log.Println("Loading game state")
|
||||
//req.ID = "load"
|
||||
//req.Data = "ok"
|
||||
//if s.RoomID != "" {
|
||||
//room := s.handler.getRoom(s.RoomID)
|
||||
//err := room.LoadGame()
|
||||
//if err != nil {
|
||||
//log.Println("[!] Cannot load game state: ", err)
|
||||
//req.Data = "error"
|
||||
//}
|
||||
//} else {
|
||||
//req.Data = "error"
|
||||
//}
|
||||
|
||||
//return req
|
||||
//})
|
||||
|
||||
//browserClient.Receive("start", func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
//s.GameName = resp.Data
|
||||
//s.RoomID = resp.RoomID
|
||||
//s.PlayerIndex = resp.PlayerIndex
|
||||
|
||||
//log.Println("Starting game")
|
||||
//// If we are connecting to overlord, request corresponding serverID based on roomID
|
||||
//if s.OverlordClient != nil {
|
||||
//roomServerID := getServerIDOfRoom(s.OverlordClient, s.RoomID)
|
||||
//log.Println("Server of RoomID ", s.RoomID, " is ", roomServerID, " while current server is ", s.ServerID)
|
||||
//// If the target serverID is different from current serverID
|
||||
//if roomServerID != "" && s.ServerID != roomServerID {
|
||||
//// TODO: Re -register
|
||||
//// Bridge Connection to the target serverID
|
||||
//go s.bridgeConnection(roomServerID, s.GameName, s.RoomID, s.PlayerIndex)
|
||||
//return
|
||||
//}
|
||||
//}
|
||||
|
||||
//// Get Room in local server
|
||||
//// TODO: check if roomID is in the current server
|
||||
//room := s.handler.getRoom(s.RoomID)
|
||||
//log.Println("Got Room from local ", room, " ID: ", s.RoomID)
|
||||
//// If room is not running
|
||||
//if room == nil {
|
||||
//// Create new room
|
||||
//room = s.handler.createNewRoom(s.GameName, s.RoomID, s.PlayerIndex)
|
||||
//// Wait for done signal from room
|
||||
//go func() {
|
||||
//<-room.Done
|
||||
//s.handler.detachRoom(room.ID)
|
||||
//}()
|
||||
//}
|
||||
|
||||
//// Attach peerconnection to room. If PC is already in room, don't detach
|
||||
//log.Println("Is PC in room", room.IsPCInRoom(s.peerconnection))
|
||||
//if !room.IsPCInRoom(s.peerconnection) {
|
||||
//s.handler.detachPeerConn(s.peerconnection)
|
||||
//room.AddConnectionToRoom(s.peerconnection, s.PlayerIndex)
|
||||
//}
|
||||
//s.RoomID = room.ID
|
||||
|
||||
//// Register room to overlord if we are connecting to overlord
|
||||
//if room != nil && s.OverlordClient != nil {
|
||||
//s.OverlordClient.Send(cws.WSPacket{
|
||||
//ID: "registerRoom",
|
||||
//Data: s.RoomID,
|
||||
//}, nil)
|
||||
//}
|
||||
//req.ID = "start"
|
||||
//req.RoomID = s.RoomID
|
||||
//req.SessionID = s.ID
|
||||
|
||||
//return req
|
||||
//})
|
||||
}
|
||||
|
||||
// NewOverlordClient returns a client connecting to browser. This connection exchanges information between clients and server
|
||||
func NewBrowserClient(c *websocket.Conn) *BrowserClient {
|
||||
return &BrowserClient{
|
||||
Client: cws.NewClient(c),
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,24 @@
|
|||
package overlord
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/giongto35/cloud-game/cws"
|
||||
"github.com/giongto35/cloud-game/overlord/gamelist"
|
||||
"github.com/gorilla/websocket"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
gameboyIndex = "./static/gameboy2.html"
|
||||
debugIndex = "./static/gameboy2.html"
|
||||
gamePath = "games"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
|
|
@ -18,6 +28,7 @@ type Server struct {
|
|||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{}
|
||||
var errNotFound = errors.New("Not found")
|
||||
|
||||
func NewServer() *Server {
|
||||
return &Server{
|
||||
|
|
@ -28,6 +39,22 @@ func NewServer() *Server {
|
|||
}
|
||||
}
|
||||
|
||||
// GetWeb returns web frontend
|
||||
func (o *Server) 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)
|
||||
}
|
||||
|
||||
// If it's overlord, handle overlord connection (from host to overlord)
|
||||
func (o *Server) WSO(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("Connected")
|
||||
|
|
@ -73,72 +100,68 @@ func (o *Server) WSO(w http.ResponseWriter, r *http.Request) {
|
|||
Data: o.roomToServer[resp.Data],
|
||||
}
|
||||
})
|
||||
|
||||
// Relay message from server to other target server
|
||||
// TODO: Generalize
|
||||
client.Receive("initwebrtc", func(resp cws.WSPacket) cws.WSPacket {
|
||||
log.Println("Overlord: Received a relay sdp request from a host")
|
||||
// TODO: Abstract
|
||||
if resp.TargetHostID != serverID {
|
||||
log.Println("Overlord: Sending relay sdp to target host")
|
||||
// relay SDP to target host and get back sdp
|
||||
// TODO: Async
|
||||
sdp := o.servers[resp.TargetHostID].SyncSend(
|
||||
resp,
|
||||
)
|
||||
|
||||
return sdp
|
||||
}
|
||||
log.Println("Overlord: Target host is overlord itself: start peerconnection")
|
||||
// If the target is in master
|
||||
// start by its old
|
||||
//localSession, err := wssession.peerconnection.StartClient(resp.Data, width, height)
|
||||
//if err != nil {
|
||||
//log.Fatalln(err)
|
||||
//}
|
||||
|
||||
//return cws.WSPacket{
|
||||
//ID: "sdp",
|
||||
//Data: localSession,
|
||||
//}
|
||||
return cws.EmptyPacket
|
||||
})
|
||||
|
||||
// TODO: use relay ID type
|
||||
// TODO: Merge sdp and start
|
||||
client.Receive("start", func(resp cws.WSPacket) cws.WSPacket {
|
||||
log.Println("Overlord: Received a relay start request from a host")
|
||||
// TODO: Abstract
|
||||
if resp.TargetHostID != serverID {
|
||||
// relay start to target host
|
||||
log.Println("Sending to target host", resp.TargetHostID, " ", resp)
|
||||
// TODO: Async
|
||||
resp := o.servers[resp.TargetHostID].SyncSend(
|
||||
resp,
|
||||
)
|
||||
|
||||
return resp
|
||||
}
|
||||
log.Println("Overlord: Target host is overlord itself: start game")
|
||||
//// If the target is in master
|
||||
//// start by its old
|
||||
//roomID, isNewRoom := startSession(wssession.peerconnection, resp.Data, resp.RoomID, resp.PlayerIndex)
|
||||
//// Bridge always access to old room
|
||||
//// TODO: log warn
|
||||
//if isNewRoom == true {
|
||||
//log.Fatal("Bridge should not spawn new room")
|
||||
//}
|
||||
|
||||
//return cws.WSPacket{
|
||||
//ID: "start",
|
||||
//RoomID: roomID,
|
||||
//}
|
||||
return cws.EmptyPacket
|
||||
})
|
||||
|
||||
client.Listen()
|
||||
}
|
||||
|
||||
func (o *Server) WS(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("Browser connected to overlord")
|
||||
//TODO: Add it back
|
||||
//defer func() {
|
||||
//if r := recover(); r != nil {
|
||||
//log.Println("Warn: Something wrong. Recovered in ", r)
|
||||
//}
|
||||
//}()
|
||||
|
||||
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()
|
||||
serverID, err := o.findBestServer()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
wssession := &Session{
|
||||
ID: sessionID,
|
||||
BrowserClient: client,
|
||||
handler: o,
|
||||
ServerID: serverID,
|
||||
}
|
||||
//defer wssession.Close()
|
||||
log.Println("New client will conect to server", wssession.ServerID)
|
||||
|
||||
wssession.RouteBrowser()
|
||||
|
||||
wssession.BrowserClient.Send(cws.WSPacket{
|
||||
ID: "gamelist",
|
||||
Data: gamelist.GetEncodedGameList(gamePath),
|
||||
}, nil)
|
||||
|
||||
wssession.BrowserClient.Listen()
|
||||
}
|
||||
|
||||
func (o *Server) findBestServer() (string, error) {
|
||||
// TODO: Find best Server by latency, currently return by ping
|
||||
if len(o.servers) == 0 {
|
||||
return "", errors.New("No server found")
|
||||
}
|
||||
|
||||
r := rand.Intn(len(o.servers))
|
||||
for k, _ := range o.servers {
|
||||
if r == 0 {
|
||||
return k, nil
|
||||
}
|
||||
r--
|
||||
}
|
||||
|
||||
return "", errors.New("No server found")
|
||||
}
|
||||
|
||||
func (o *Server) cleanConnection(client *cws.Client, serverID string) {
|
||||
log.Println("Unregister server from overlord")
|
||||
// Remove serverID from servers
|
||||
21
overlord/session.go
Normal file
21
overlord/session.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package overlord
|
||||
|
||||
// Session represents a session connected from the browser to the current server
|
||||
// It requires one connection to browser and one connection to the overlord
|
||||
// connection to browser is 1-1. connection to overlord is n - 1
|
||||
// Peerconnection can be from other server to ensure better latency
|
||||
type Session struct {
|
||||
ID string
|
||||
BrowserClient *BrowserClient
|
||||
WorkerClient *WorkerClient
|
||||
//OverlordClient *OverlordClient
|
||||
//peerconnection *webrtc.WebRTC
|
||||
|
||||
// TODO: Decouple this
|
||||
handler *Server
|
||||
|
||||
ServerID string
|
||||
GameName string
|
||||
RoomID string
|
||||
PlayerIndex int
|
||||
}
|
||||
176
overlord/worker.go
Normal file
176
overlord/worker.go
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
package overlord
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/giongto35/cloud-game/cws"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type WorkerClient struct {
|
||||
*cws.Client
|
||||
}
|
||||
|
||||
// RouteWorker are all routes server received from worker
|
||||
func (s *Session) RouteWorker() {
|
||||
iceCandidates := [][]byte{}
|
||||
|
||||
workerClient := s.WorkerClient
|
||||
|
||||
workerClient.Receive("heartbeat", func(resp cws.WSPacket) cws.WSPacket {
|
||||
return resp
|
||||
})
|
||||
|
||||
workerClient.Receive("icecandidate", func(resp cws.WSPacket) cws.WSPacket {
|
||||
log.Println("Received candidates ", resp.Data)
|
||||
iceCandidates = append(iceCandidates, []byte(resp.Data))
|
||||
return cws.EmptyPacket
|
||||
})
|
||||
|
||||
workerClient.Receive("initwebrtc", func(resp cws.WSPacket) cws.WSPacket {
|
||||
log.Println("Overlord: Received sdp request from a worker")
|
||||
log.Println("Overlord: Sending back sdp to browser")
|
||||
s.BrowserClient.Send(resp, nil)
|
||||
|
||||
return cws.EmptyPacket
|
||||
})
|
||||
|
||||
workerClient.Receive("quit", func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
log.Println("Received quit", req)
|
||||
s.GameName = resp.Data
|
||||
s.RoomID = resp.RoomID
|
||||
s.PlayerIndex = resp.PlayerIndex
|
||||
|
||||
// TODO:
|
||||
//room := s.handler.getRoom(s.RoomID)
|
||||
//if room.IsPCInRoom(s.peerconnection) {
|
||||
//s.handler.detachPeerConn(s.peerconnection)
|
||||
//}
|
||||
log.Println("Sending to target host", resp.TargetHostID, " ", resp)
|
||||
resp = s.handler.servers[resp.TargetHostID].SyncSend(
|
||||
resp,
|
||||
)
|
||||
|
||||
return cws.EmptyPacket
|
||||
})
|
||||
|
||||
//browserClient.Receive("initwebrtc", func(resp cws.WSPacket) cws.WSPacket {
|
||||
//log.Println("Received user SDP")
|
||||
//localSession, err := s.peerconnection.StartClient(resp.Data, iceCandidates, config.Width, config.Height)
|
||||
//if err != nil {
|
||||
//if err != nil {
|
||||
//log.Println("Error: Cannot create new webrtc session", err)
|
||||
//return cws.EmptyPacket
|
||||
//}
|
||||
//}
|
||||
|
||||
//return cws.WSPacket{
|
||||
//ID: "sdp",
|
||||
//Data: localSession,
|
||||
//SessionID: s.ID,
|
||||
//}
|
||||
//})
|
||||
|
||||
// TODO: Add save and load
|
||||
//browserClient.Receive("save", func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
//log.Println("Saving game state")
|
||||
//req.ID = "save"
|
||||
//req.Data = "ok"
|
||||
//if s.RoomID != "" {
|
||||
//room := s.handler.getRoom(s.RoomID)
|
||||
//if room == nil {
|
||||
//return
|
||||
//}
|
||||
//err := room.SaveGame()
|
||||
//if err != nil {
|
||||
//log.Println("[!] Cannot save game state: ", err)
|
||||
//req.Data = "error"
|
||||
//}
|
||||
//} else {
|
||||
//req.Data = "error"
|
||||
//}
|
||||
|
||||
//return req
|
||||
//})
|
||||
|
||||
//browserClient.Receive("load", func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
//log.Println("Loading game state")
|
||||
//req.ID = "load"
|
||||
//req.Data = "ok"
|
||||
//if s.RoomID != "" {
|
||||
//room := s.handler.getRoom(s.RoomID)
|
||||
//err := room.LoadGame()
|
||||
//if err != nil {
|
||||
//log.Println("[!] Cannot load game state: ", err)
|
||||
//req.Data = "error"
|
||||
//}
|
||||
//} else {
|
||||
//req.Data = "error"
|
||||
//}
|
||||
|
||||
//return req
|
||||
//})
|
||||
|
||||
//browserClient.Receive("start", func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
//s.GameName = resp.Data
|
||||
//s.RoomID = resp.RoomID
|
||||
//s.PlayerIndex = resp.PlayerIndex
|
||||
|
||||
//log.Println("Starting game")
|
||||
//// If we are connecting to overlord, request corresponding serverID based on roomID
|
||||
//if s.OverlordClient != nil {
|
||||
//roomServerID := getServerIDOfRoom(s.OverlordClient, s.RoomID)
|
||||
//log.Println("Server of RoomID ", s.RoomID, " is ", roomServerID, " while current server is ", s.ServerID)
|
||||
//// If the target serverID is different from current serverID
|
||||
//if roomServerID != "" && s.ServerID != roomServerID {
|
||||
//// TODO: Re -register
|
||||
//// Bridge Connection to the target serverID
|
||||
//go s.bridgeConnection(roomServerID, s.GameName, s.RoomID, s.PlayerIndex)
|
||||
//return
|
||||
//}
|
||||
//}
|
||||
|
||||
//// Get Room in local server
|
||||
//// TODO: check if roomID is in the current server
|
||||
//room := s.handler.getRoom(s.RoomID)
|
||||
//log.Println("Got Room from local ", room, " ID: ", s.RoomID)
|
||||
//// If room is not running
|
||||
//if room == nil {
|
||||
//// Create new room
|
||||
//room = s.handler.createNewRoom(s.GameName, s.RoomID, s.PlayerIndex)
|
||||
//// Wait for done signal from room
|
||||
//go func() {
|
||||
//<-room.Done
|
||||
//s.handler.detachRoom(room.ID)
|
||||
//}()
|
||||
//}
|
||||
|
||||
//// Attach peerconnection to room. If PC is already in room, don't detach
|
||||
//log.Println("Is PC in room", room.IsPCInRoom(s.peerconnection))
|
||||
//if !room.IsPCInRoom(s.peerconnection) {
|
||||
//s.handler.detachPeerConn(s.peerconnection)
|
||||
//room.AddConnectionToRoom(s.peerconnection, s.PlayerIndex)
|
||||
//}
|
||||
//s.RoomID = room.ID
|
||||
|
||||
//// Register room to overlord if we are connecting to overlord
|
||||
//if room != nil && s.OverlordClient != nil {
|
||||
//s.OverlordClient.Send(cws.WSPacket{
|
||||
//ID: "registerRoom",
|
||||
//Data: s.RoomID,
|
||||
//}, nil)
|
||||
//}
|
||||
//req.ID = "start"
|
||||
//req.RoomID = s.RoomID
|
||||
//req.SessionID = s.ID
|
||||
|
||||
//return req
|
||||
//})
|
||||
}
|
||||
|
||||
// NewWorkerClient returns a client connecting to worker. This connection exchanges information between workers and server
|
||||
func NewWorkerClient(c *websocket.Conn) *WorkerClient {
|
||||
return &WorkerClient{
|
||||
Client: cws.NewClient(c),
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue