diff --git a/main.go b/main.go index 45ef571a..7718e0f5 100644 --- a/main.go +++ b/main.go @@ -168,9 +168,10 @@ func startSession(webRTC *webrtc.WebRTC, gameName string, roomID string, playerI // Session represents a session connected from the browser to the current server type Session struct { - client *Client - oclient *Client - ServerID string + client *Client + oclient *Client + peerconnection *webrtc.WebRTC + ServerID string } // Handle normal traffic (from browser to host) @@ -186,10 +187,11 @@ func ws(w http.ResponseWriter, r *http.Request) { var playerIndex int // Create connection to overlord - client := NewClient(c, webrtc.NewWebRTC()) + client := NewClient(c) wssession := &Session{ - client: client, + client: client, + peerconnection: webrtc.NewWebRTC(), // The server session is maintaining } @@ -199,7 +201,7 @@ func ws(w http.ResponseWriter, r *http.Request) { client.receive("initwebrtc", func(resp WSPacket) WSPacket { log.Println("Received user SDP") - localSession, err := client.peerconnection.StartClient(resp.Data, width, height) + localSession, err := wssession.peerconnection.StartClient(resp.Data, width, height) if err != nil { log.Fatalln(err) } @@ -252,15 +254,15 @@ func ws(w http.ResponseWriter, r *http.Request) { //log.Println("Ping from server with game:", gameName) //res.ID = "pong" log.Println("Starting game") - roomServerID := GetServerIDOfRoom(wssession.oclient, roomID) + roomServerID := getServerIDOfRoom(wssession.oclient, roomID) log.Println("Server of RoomID ", roomID, " is ", roomServerID) if roomServerID != "" && wssession.ServerID != roomServerID { // TODO: Re -register - bridgeConnection(wssession, roomServerID) + go bridgeConnection(wssession, roomServerID, gameName, roomID, playerIndex) return } - roomID, isNewRoom = startSession(client.peerconnection, gameName, roomID, playerIndex) + roomID, isNewRoom = startSession(wssession.peerconnection, gameName, roomID, playerIndex) if isNewRoom { wssession.oclient.send(WSPacket{ ID: "registerRoom", @@ -383,7 +385,7 @@ func removeSession(w *webrtc.WebRTC, room *Room) { } } -func GetServerIDOfRoom(oc *Client, roomID string) string { +func getServerIDOfRoom(oc *Client, roomID string) string { packet := oc.syncSend( WSPacket{ ID: "getRoom", @@ -394,8 +396,37 @@ func GetServerIDOfRoom(oc *Client, roomID string) string { return packet.Data } -func bridgeConnection(session *Session, serverID string) { +func bridgeConnection(session *Session, serverID string, gameName string, roomID string, playerIndex int) { + log.Println("Bridging connection to other Host ", serverID) + client := session.client + oclient := session.oclient // Ask client to init + + log.Println("Requesting offer to browser", serverID) + resp := client.syncSend(WSPacket{ + ID: "requestOffer", + Data: "", + }) + + log.Println("Sending offer to overlord to relay message to target host", resp.TargetHostID) + // Ask overlord to relay SDP packet to serverID + resp.TargetHostID = serverID + remoteTargetSDP := oclient.syncSend(resp) + log.Println("Got back remote host SDP, sending to browser", remoteTargetSDP.Data) + // Send back remote SDP of remote server to browser + client.syncSend(WSPacket{ + ID: "sdp", + Data: remoteTargetSDP.Data, + }) + log.Println("Init session done, start game on target host") + + oclient.syncSend(WSPacket{ + ID: "start", + Data: gameName, + RoomID: roomID, + PlayerIndex: playerIndex, + }) + log.Println("Game is started on remote host") } const overlordHost = "ws://localhost:9000/wso" @@ -415,7 +446,7 @@ func (s *Session) NewOverlordClient() { if err != nil { log.Println("Cannot connect to overlord") } - oclient := NewClient(oc, webrtc.NewWebRTC()) + oclient := NewClient(oc) oclient.send( WSPacket{ ID: "ping", @@ -437,6 +468,46 @@ func (s *Session) NewOverlordClient() { }, ) + // Received from overlord the sdp. This is happens when bridging + // TODO: refactor + oclient.receive( + "initwebrtc", + func(resp WSPacket) (req WSPacket) { + log.Println("Received a sdp request from overlord") + log.Println("Start peerconnection from the sdp") + + localSession, err := s.peerconnection.StartClient(resp.Data, width, height) + if err != nil { + log.Fatalln(err) + } + + return WSPacket{ + ID: "sdp", + Data: localSession, + } + }, + ) + + // Received start from overlord. This is happens when bridging + // TODO: refactor + oclient.receive( + "start", + func(resp WSPacket) (req WSPacket) { + log.Println("Received a start request from overlord") + log.Println("Add the connection to current room on the host") + + roomID, isNewRoom := startSession(s.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") + } + + req.ID = "start" + req.RoomID = roomID + return req + }, + ) go oclient.listen() s.oclient = oclient diff --git a/overlord.go b/overlord.go index 5878ba8d..1955ce69 100644 --- a/overlord.go +++ b/overlord.go @@ -13,7 +13,7 @@ import ( var roomToServer = map[string]string{} // servers are the map serverID to server Client -var servers = map[string]Client{} +var servers = map[string]*Client{} // If it's overlord, handle overlord connection (from host to overlord) func wso(w http.ResponseWriter, r *http.Request) { @@ -29,7 +29,14 @@ func wso(w http.ResponseWriter, r *http.Request) { serverID := strconv.Itoa(rand.Int()) log.Println("A new server connected ", serverID) - client := NewClient(c, webrtc.NewWebRTC()) + client := NewClient(c) + servers[serverID] = client + + wssession := &Session{ + client: client, + peerconnection: webrtc.NewWebRTC(), + // The server session is maintaining + } client.send( WSPacket{ @@ -61,5 +68,62 @@ func wso(w http.ResponseWriter, r *http.Request) { } }) + client.receive("initwebrtc", func(resp WSPacket) 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.TargetHostID) + // relay SDP to target host and get back sdp + // TODO: Async + sdp := servers[resp.TargetHostID].syncSend( + resp, + ) + + return sdp + } + 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) + } + + return WSPacket{ + ID: "sdp", + Data: localSession, + } + }) + + // TODO: use relay ID type + // TODO: Merge sdp and start + client.receive("start", func(resp WSPacket) 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, + ) + + 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") + } + + return WSPacket{ + ID: "start", + RoomID: roomID, + } + }) + client.listen() } diff --git a/static/js/ws.js b/static/js/ws.js index b50cd19e..936ab8b9 100644 --- a/static/js/ws.js +++ b/static/js/ws.js @@ -1,4 +1,5 @@ var pc; +var curPacketID = ""; // web socket conn = new WebSocket(`ws://${location.host}/ws`); @@ -27,10 +28,10 @@ conn.onmessage = e => { log("Got remote sdp"); pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(atob(d["data"])))); break; - //case "requestOffer": - //pc.createOffer({offerToReceiveVideo: true, offerToReceiveAudio: false}).then(d => { - //pc.setLocalDescription(d).catch(log); - //}) + case "requestOffer": + curPacketID = d["packet_id"]; + log("Received request offer ", curPacketID) + startWebRTC(); //case "sdpremote": //log("Got remote sdp"); @@ -108,7 +109,8 @@ function startWebRTC() { session = btoa(JSON.stringify(pc.localDescription)); localSessionDescription = session; log("Send SDP to remote peer"); - conn.send(JSON.stringify({"id": "initwebrtc", "data": session})); + // TODO: Fix curPacketID + conn.send(JSON.stringify({"id": "initwebrtc", "data": session, "packet_id": curPacketID})); } else { console.log(JSON.stringify(event.candidate)); } diff --git a/ws.go b/ws.go index e52046f0..4f088f9f 100644 --- a/ws.go +++ b/ws.go @@ -7,15 +7,12 @@ import ( "strconv" "time" - "github.com/giongto35/cloud-game/webrtc" "github.com/gorilla/websocket" ) type Client struct { conn *websocket.Conn - peerconnection *webrtc.WebRTC - // sendCallback is callback based on packetID sendCallback map[string]func(req WSPacket) // recvCallback is callback when receive based on ID of the packet @@ -30,20 +27,19 @@ type WSPacket struct { PlayerIndex int `json:"player_index"` TargetHostID string `json:"target_id"` - PacketID string + PacketID string `json:"packet_id"` } var EmptyPacket = WSPacket{} -func NewClient(conn *websocket.Conn, webrtc *webrtc.WebRTC) *Client { +func NewClient(conn *websocket.Conn) *Client { sendCallback := map[string]func(WSPacket){} recvCallback := map[string]func(WSPacket){} return &Client{ conn: conn, - peerconnection: webrtc, - sendCallback: sendCallback, - recvCallback: recvCallback, + sendCallback: sendCallback, + recvCallback: recvCallback, } } @@ -94,6 +90,7 @@ func (c *Client) syncSend(request WSPacket) (response WSPacket) { func (c *Client) listen() { for { + log.Println("Waiting for message") _, rawMsg, err := c.conn.ReadMessage() if err != nil { log.Println("[!] read:", err) @@ -109,6 +106,8 @@ func (c *Client) listen() { if callback, ok := c.sendCallback[wspacket.PacketID]; ok { callback(wspacket) delete(c.sendCallback, wspacket.PacketID) + // Skip receiveCallback to avoid duplication + continue } // Check if some receiver with the ID is registered if callback, ok := c.recvCallback[wspacket.ID]; ok {