From dd415f1cce6493e96a6563fa367211bec4ad253d Mon Sep 17 00:00:00 2001 From: Nguyen Huu Thanh Date: Wed, 1 May 2019 11:02:37 +0800 Subject: [PATCH] WIP --- cmd/main.go | 74 ++++++++++++++++------------- cmd/main_test.go | 63 ++++++++++++++---------- handler/browser.go | 62 +++++++++++++----------- handler/handlers.go | 21 ++++---- handler/overlord.go | 2 +- overlord/overlord.go | 111 +++++++++++++++++++++++++------------------ 6 files changed, 191 insertions(+), 142 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 509e999d..ad070a5d 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -3,7 +3,6 @@ package main import ( "flag" "log" - "math/rand" "net/http" _ "net/http/pprof" "strings" @@ -29,6 +28,47 @@ var writeWait = 30 * time.Second var IsOverlord = false var upgrader = websocket.Upgrader{} +// initilizeOverlord setup an overlord server +func initilizeOverlord() { + overlord := overlord.NewServer() + + log.Println("http://localhost:9000") + + // Can consider Overlord works as server but it is complicated + http.HandleFunc("/wso", overlord.WSO) + http.ListenAndServe(":9000", nil) +} + +func createOverlordConnection() (*websocket.Conn, error) { + c, _, err := websocket.DefaultDialer.Dial(*config.OverlordHost, nil) + if err != nil { + return nil, err + } + + return c, nil +} + +// initializeServer setup a server +func initializeServer() { + conn, err := createOverlordConnection() + if err != nil { + log.Println("Cannot connect to overlord") + log.Println("Run as a single server") + } + + handler := handler.NewHandler(conn) + + // ignore origin + 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) +} + func main() { flag.Parse() log.Println("Usage: ./game [debug]") @@ -40,6 +80,7 @@ func main() { if *config.OverlordHost == "overlord" { log.Println("Running as overlord ") + initilizeOverlord() IsOverlord = true } else { if strings.HasPrefix(*config.OverlordHost, "ws") && !strings.HasSuffix(*config.OverlordHost, "wso") { @@ -48,35 +89,4 @@ func main() { log.Println("Running as slave ") IsOverlord = false } - - handler, err := handler.NewHandler(IsOverlord) - rand.Seed(time.Now().UTC().UnixNano()) - - // ignore origin - 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) - - if !IsOverlord { - conn, err := createOverlordConnection() - if err != nil { - log.Println("Cannot connect to overlord") - log.Println("Run as a single server") - oclient = nil - } else { - oclient = NewOverlordClient(conn) - } - } - - if !IsOverlord { - log.Println("http://localhost:" + *config.Port) - http.ListenAndServe(":"+*config.Port, nil) - } else { - log.Println("http://localhost:9000") - // Overlord expose one more path for handle overlord connections - http.HandleFunc("/wso", overlord.WSO) - http.ListenAndServe(":9000", nil) - } } diff --git a/cmd/main_test.go b/cmd/main_test.go index 715d1a42..45e16b07 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -8,6 +8,9 @@ import ( "testing" "time" + "github.com/giongto35/cloud-game/cws" + "github.com/giongto35/cloud-game/handler" + "github.com/giongto35/cloud-game/overlord" gamertc "github.com/giongto35/cloud-game/webrtc" "github.com/gorilla/websocket" "github.com/pion/webrtc" @@ -17,26 +20,36 @@ var host = "http://localhost:8000" var webrtcconfig = webrtc.Configuration{ICEServers: []webrtc.ICEServer{{URLs: []string{"stun:stun.l.google.com:19302"}}}} func initOverlord() *httptest.Server { - overlord := httptest.NewServer(http.HandlerFunc(wso)) + server := overlord.NewServer() + overlord := httptest.NewServer(http.HandlerFunc(server.WSO)) return overlord } -func initServer(t *testing.T, overlordURL string) *httptest.Server { - if overlordURL == "" { - oclient = nil - } else { - u := "ws" + strings.TrimPrefix(overlordURL, "http") - fmt.Println("connecting to overlord: ", u) +func initServer(t *testing.T, oclient *handler.OverlordClient) *httptest.Server { + conn := connectTestOverlordServer() + handler, err := handler.NewHandler(oclient) + if err != nil { + t.Fatalf("%v", err) + } + server := httptest.NewServer(http.HandlerFunc(handler.WS)) + return server +} - oconn, _, err := websocket.DefaultDialer.Dial(u, nil) - if err != nil { - t.Fatalf("%v", err) - } - oclient = NewOverlordClient(oconn) +func connectTestOverlordServer(t *testing.T, overlordURL string) *handler.OverlordClient { + if overlordURL == "" { + return nil + } else { + overlordURL = "ws" + strings.TrimPrefix(overlordURL, "http") + fmt.Println("connecting to overlord: ", overlordURL) } - server := httptest.NewServer(http.HandlerFunc(ws)) - return server + oconn, _, err := websocket.DefaultDialer.Dial(overlordURL, nil) + if err != nil { + t.Fatalf("%v", err) + } + defer oconn.Close() + + return oconn } func initClient(t *testing.T, host string) { @@ -69,17 +82,17 @@ func initClient(t *testing.T, host string) { } // Send offer to server - client := NewClient(ws) - go client.listen() + client := cws.NewClient(ws) + go client.Listen() fmt.Println("Sending offer...") - client.send(WSPacket{ + client.Send(cws.WSPacket{ ID: "initwebrtc", Data: gamertc.Encode(offer), }, nil) fmt.Println("Waiting sdp...") - client.receive("sdp", func(resp WSPacket) WSPacket { + client.Receive("sdp", func(resp cws.WSPacket) cws.WSPacket { fmt.Println("received", resp.Data) answer := webrtc.SessionDescription{} gamertc.Decode(resp.Data, &answer) @@ -89,19 +102,19 @@ func initClient(t *testing.T, host string) { panic(err) } - return EmptyPacket + return cws.EmptyPacket }) time.Sleep(time.Second * 3) fmt.Println("Sending start...") roomID := make(chan string) - client.send(WSPacket{ + client.Send(cws.WSPacket{ ID: "start", Data: "Contra.nes", RoomID: "", PlayerIndex: 1, - }, func(resp WSPacket) { + }, func(resp cws.WSPacket) { fmt.Println("Received response") fmt.Println("RoomID:", resp.RoomID) roomID <- resp.RoomID @@ -119,8 +132,7 @@ func initClient(t *testing.T, host string) { func TestSingleServerNoOverlord(t *testing.T) { // Init slave server - oclient = nil - s := initServer(t, "") + s := initServer(t, nil) defer s.Close() initClient(t, s.URL) @@ -129,12 +141,13 @@ func TestSingleServerNoOverlord(t *testing.T) { func TestSingleServerOneOverlord(t *testing.T) { o := initOverlord() defer o.Close() + + oconn := connectOverlord(t, o.URL) // Init slave server - s := initServer(t, o.URL) + s := initServer(t, oconn) defer s.Close() initClient(t, s.URL) - oclient.conn.Close() } //func TestTwoServerOneOverlord(t *testing.T) { diff --git a/handler/browser.go b/handler/browser.go index 8425a4f0..082c22a5 100644 --- a/handler/browser.go +++ b/handler/browser.go @@ -22,33 +22,8 @@ type BrowserClient struct { playerIndex int } -// NewOverlordClient returns a client connecting to browser. This connection exchanges information between clients and server -func NewBrowserClient(c *websocket.Conn, overlordClient *OverlordClient) *BrowserClient { - roomID := "" - gameName := "" - playerIndex := 0 - // Create connection to overlord - browserClient := &BrowserClient{ - Client: cws.NewClient(c), - gameName: "", - roomID: "", - playerIndex: 0, - } - - //sessionID := strconv.Itoa(rand.Int()) - sessionID := uuid.Must(uuid.NewV4()).String() - - wssession := &Session{ - BrowserClient: browserClient, - OverlordClient: overlordClient, - peerconnection: webrtc.NewWebRTC(), - // The server session is maintaining - } - - browserClient.Send(cws.WSPacket{ - ID: "gamelist", - Data: gamelist.GetEncodedGameList(), - }, nil) +func (s *Session) RegisterBrowserClient() { + browserClient := s.BrowserClient browserClient.Receive("heartbeat", func(resp cws.WSPacket) cws.WSPacket { return resp @@ -56,7 +31,7 @@ func NewBrowserClient(c *websocket.Conn, overlordClient *OverlordClient) *Browse browserClient.Receive("initwebrtc", func(resp cws.WSPacket) cws.WSPacket { log.Println("Received user SDP") - localSession, err := wssession.peerconnection.StartClient(resp.Data, config.Width, config.Height) + localSession, err := s.peerconnection.StartClient(resp.Data, config.Width, config.Height) if err != nil { log.Fatalln(err) } @@ -64,7 +39,7 @@ func NewBrowserClient(c *websocket.Conn, overlordClient *OverlordClient) *Browse return cws.WSPacket{ ID: "sdp", Data: localSession, - SessionID: sessionID, + SessionID: s.SessionID, } }) @@ -152,5 +127,34 @@ func NewBrowserClient(c *websocket.Conn, overlordClient *OverlordClient) *Browse return req }) +} + +// NewOverlordClient returns a client connecting to browser. This connection exchanges information between clients and server +func NewBrowserClient(c *websocket.Conn, overlordClient *OverlordClient) *BrowserClient { + roomID := "" + gameName := "" + playerIndex := 0 + // Create connection to overlord + browserClient := &BrowserClient{ + Client: cws.NewClient(c), + gameName: "", + roomID: "", + playerIndex: 0, + } + + //sessionID := strconv.Itoa(rand.Int()) + sessionID := uuid.Must(uuid.NewV4()).String() + + wssession := &Session{ + BrowserClient: browserClient, + OverlordClient: overlordClient, + peerconnection: webrtc.NewWebRTC(), + // The server session is maintaining + } + + browserClient.Send(cws.WSPacket{ + ID: "gamelist", + Data: gamelist.GetEncodedGameList(), + }, nil) return browserClient } diff --git a/handler/handlers.go b/handler/handlers.go index 47ac6dc9..b9f4ab8c 100644 --- a/handler/handlers.go +++ b/handler/handlers.go @@ -30,7 +30,7 @@ var upgrader = websocket.Upgrader{} // ID to peerconnection //var peerconnections = map[string]*webrtc.WebRTC{} -var oclient *OverlordClient +//var oclient *OverlordClient type Handler struct { oClient *OverlordClient @@ -38,18 +38,21 @@ type Handler struct { serverID string // ID to peerconnection peerconnections map[string]*webrtc.WebRTC + // Session + wssession Session } -func NewHandler() (*Handler, error) { - conn, err := createOverlordConnection() - if err != nil { - return nil, err - } +// NewHandler returns a new server +func NewHandler(overlordConn *websocket.Conn) *Handler { + //conn, err := createOverlordConnection() + //if err != nil { + //return nil, err + //} return &Handler{ - oClient: NewOverlordClient(conn), + oClient: NewOverlordClient(overlordConn), rooms: map[string]*Room{}, peerconnections: map[string]*webrtc.WebRTC{}, - }, nil + } } // GetWeb returns web frontend @@ -61,7 +64,7 @@ func (h *Handler) GetWeb(w http.ResponseWriter, r *http.Request) { w.Write(bs) } -// Handle normal traffic (from browser to host) +// 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 { diff --git a/handler/overlord.go b/handler/overlord.go index 4d2575fe..a6f0df8b 100644 --- a/handler/overlord.go +++ b/handler/overlord.go @@ -21,7 +21,7 @@ func NewOverlordClient(oc *websocket.Conn) *OverlordClient { return oclient } -func (h *Handler) Register(oclient *OverlordClient) { +func (h *Handler) RegisterOverlordClient(oclient *OverlordClient) { // Received from overlord the serverID oclient.Receive( diff --git a/overlord/overlord.go b/overlord/overlord.go index 3e67de4a..b1e459a2 100644 --- a/overlord/overlord.go +++ b/overlord/overlord.go @@ -8,16 +8,26 @@ import ( "strconv" "github.com/giongto35/cloud-game/cws" - "github.com/giongto35/cloud-game/webrtc" + "github.com/gorilla/websocket" ) -var roomToServer = map[string]string{} +type Server struct { + roomToServer map[string]string + // servers are the map serverID to server Client + servers map[string]*cws.Client +} -// servers are the map serverID to server Client -var servers = map[string]*cws.Client{} +var upgrader = websocket.Upgrader{} + +func NewServer() *Server { + return &Server{ + servers: map[string]*cws.Client{}, + roomToServer: map[string]string{}, + } +} // If it's overlord, handle overlord connection (from host to overlord) -func WSO(w http.ResponseWriter, r *http.Request) { +func (o *Server) WSO(w http.ResponseWriter, r *http.Request) { fmt.Println("Connected") c, err := upgrader.Upgrade(w, r, nil) if err != nil { @@ -26,51 +36,58 @@ func WSO(w http.ResponseWriter, r *http.Request) { } defer c.Close() - // register new server + // Register new server serverID := strconv.Itoa(rand.Int()) log.Println("A new server connected ", serverID) - client := NewClient(c) - servers[serverID] = client + // Register to servers map the client connection + client := cws.NewClient(c) + o.servers[serverID] = client - wssession := &Session{ - client: client, - peerconnection: webrtc.NewWebRTC(), - // The server session is maintaining - } + //wssession := &Session{ + //client: client, + //peerconnection: webrtc.NewWebRTC(), + //// The server session is maintaining + //} - client.send( - WSPacket{ + // Sendback the ID to server + client.Send( + cws.WSPacket{ ID: "serverID", Data: serverID, }, nil, ) - client.receive("registerRoom", func(resp WSPacket) WSPacket { + // registerRoom event from a server, when server created a new room. + // RoomID is global so it is managed by overlord. + client.Receive("registerRoom", func(resp cws.WSPacket) cws.WSPacket { log.Println("Received registerRoom ", resp.Data, serverID) - roomToServer[resp.Data] = serverID - return WSPacket{ + o.roomToServer[resp.Data] = serverID + return cws.WSPacket{ ID: "registerRoom", } }) - client.receive("getRoom", func(resp WSPacket) WSPacket { + // getRoom returns the server ID based on requested roomID. + client.Receive("getRoom", func(resp cws.WSPacket) cws.WSPacket { log.Println("Received a getroom request") - return WSPacket{ + return cws.WSPacket{ ID: "getRoom", - Data: roomToServer[resp.Data], + Data: o.roomToServer[resp.Data], } }) - client.receive("initwebrtc", func(resp WSPacket) WSPacket { + // Relay message from server to other target server + // TODO: Generalize + client.Receive("initwebrtc", func(resp cws.WSPacket) cws.WSPacket { log.Println("Received a relay sdp request from a host") // TODO: Abstract if resp.TargetHostID != serverID { log.Println("sending relay sdp to target host", resp) // relay SDP to target host and get back sdp // TODO: Async - sdp := servers[resp.TargetHostID].syncSend( + sdp := o.servers[resp.TargetHostID].SyncSend( resp, ) @@ -79,46 +96,48 @@ func WSO(w http.ResponseWriter, r *http.Request) { log.Println("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) - } + //localSession, err := wssession.peerconnection.StartClient(resp.Data, width, height) + //if err != nil { + //log.Fatalln(err) + //} - return WSPacket{ - ID: "sdp", - Data: localSession, - } + //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 WSPacket) WSPacket { + client.Receive("start", func(resp cws.WSPacket) cws.WSPacket { log.Println("Received a relay start request from a host") // TODO: Abstract if resp.TargetHostID != serverID { // relay SDP to target host and get back sdp // TODO: Async - resp := servers[resp.TargetHostID].syncSend( + resp := o.servers[resp.TargetHostID].SyncSend( resp, ) return resp } log.Println("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") - } + //// 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 WSPacket{ - ID: "start", - RoomID: roomID, - } + //return cws.WSPacket{ + //ID: "start", + //RoomID: roomID, + //} + return cws.EmptyPacket }) - client.listeồi + client.Listen() }