mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-07-20 16:54:25 +00:00
WIP
This commit is contained in:
parent
1447406b02
commit
74b66ae6ea
17 changed files with 351 additions and 922 deletions
546
cmd/main.go
546
cmd/main.go
|
|
@ -1,34 +1,21 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"image"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/giongto35/cloud-game/config"
|
||||
"github.com/giongto35/cloud-game/util"
|
||||
"github.com/giongto35/cloud-game/webrtc"
|
||||
"github.com/giongto35/cloud-game/handler"
|
||||
"github.com/giongto35/cloud-game/overlord"
|
||||
"github.com/gorilla/websocket"
|
||||
pionRTC "github.com/pion/webrtc"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
|
||||
"gopkg.in/hraban/opus.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
width = 256
|
||||
height = 240
|
||||
scale = 3
|
||||
title = "NES"
|
||||
gameboyIndex = "./static/gameboy.html"
|
||||
debugIndex = "./static/index_ws.html"
|
||||
)
|
||||
|
|
@ -42,28 +29,6 @@ var writeWait = 30 * time.Second
|
|||
var IsOverlord = false
|
||||
var upgrader = websocket.Upgrader{}
|
||||
|
||||
// Room is a game session. multi webRTC sessions can connect to a same game.
|
||||
// A room stores all the channel for interaction between all webRTCs session and emulator
|
||||
type Room struct {
|
||||
imageChannel chan *image.RGBA
|
||||
audioChannel chan float32
|
||||
inputChannel chan int
|
||||
// Done channel is to fire exit event when there is no webRTC session running
|
||||
Done chan struct{}
|
||||
|
||||
rtcSessions []*webrtc.WebRTC
|
||||
sessionsLock *sync.Mutex
|
||||
|
||||
director *emulator.Director
|
||||
}
|
||||
|
||||
var rooms = map[string]*Room{}
|
||||
|
||||
// ID to peerconnection
|
||||
var peerconnections = map[string]*webrtc.WebRTC{}
|
||||
var serverID = ""
|
||||
var oclient *Client
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
log.Println("Usage: ./game [debug]")
|
||||
|
|
@ -84,15 +49,15 @@ func main() {
|
|||
IsOverlord = false
|
||||
}
|
||||
|
||||
handler, err := handler.NewHandler(IsOverlord)
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
rooms = map[string]*Room{}
|
||||
|
||||
// ignore origin
|
||||
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
|
||||
|
||||
http.HandleFunc("/", getWeb)
|
||||
http.HandleFunc("/", handler.GetWeb)
|
||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
|
||||
http.HandleFunc("/ws", ws)
|
||||
http.HandleFunc("/ws", handler.WS)
|
||||
|
||||
if !IsOverlord {
|
||||
conn, err := createOverlordConnection()
|
||||
|
|
@ -105,512 +70,13 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
log.Println("oclient ", oclient)
|
||||
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", wso)
|
||||
http.HandleFunc("/wso", overlord.WSO)
|
||||
http.ListenAndServe(":9000", nil)
|
||||
}
|
||||
}
|
||||
|
||||
func getWeb(w http.ResponseWriter, r *http.Request) {
|
||||
bs, err := ioutil.ReadFile(indexFN)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
w.Write(bs)
|
||||
}
|
||||
|
||||
// init initilizes a room returns roomID
|
||||
func initRoom(roomID, gameName string) string {
|
||||
// if no roomID is given, generate it
|
||||
if roomID == "" {
|
||||
roomID = generateRoomID()
|
||||
}
|
||||
log.Println("Init new room", roomID, gameName)
|
||||
imageChannel := make(chan *image.RGBA, 100)
|
||||
audioChannel := make(chan float32, emulator.SampleRate)
|
||||
inputChannel := make(chan int, 100)
|
||||
|
||||
// create director
|
||||
director := emulator.NewDirector(roomID, imageChannel, audioChannel, inputChannel)
|
||||
|
||||
room := &Room{
|
||||
imageChannel: imageChannel,
|
||||
audioChannel: audioChannel,
|
||||
inputChannel: inputChannel,
|
||||
rtcSessions: []*webrtc.WebRTC{},
|
||||
sessionsLock: &sync.Mutex{},
|
||||
director: director,
|
||||
Done: make(chan struct{}),
|
||||
}
|
||||
rooms[roomID] = room
|
||||
|
||||
go room.startVideo()
|
||||
go room.startAudio()
|
||||
go director.Start([]string{"games/" + gameName})
|
||||
|
||||
return roomID
|
||||
}
|
||||
|
||||
// isRoomRunning check if there is any running sessions.
|
||||
// TODO: If we remove sessions from room anytime a session is closed, we can check if the sessions list is empty or not.
|
||||
func isRoomRunning(roomID string) bool {
|
||||
// If no roomID is registered
|
||||
if _, ok := rooms[roomID]; !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// If there is running session
|
||||
for _, s := range rooms[roomID].rtcSessions {
|
||||
if !s.IsClosed() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// startSession handles one session call
|
||||
func startSession(webRTC *webrtc.WebRTC, gameName string, roomID string, playerIndex int) (rRoomID string, isNewRoom bool) {
|
||||
isNewRoom = false
|
||||
cleanSession(webRTC)
|
||||
// If the roomID is empty,
|
||||
// or the roomID doesn't have any running sessions (room was closed)
|
||||
// we spawn a new room
|
||||
if roomID == "" || !isRoomRunning(roomID) {
|
||||
roomID = initRoom(roomID, gameName)
|
||||
isNewRoom = true
|
||||
}
|
||||
|
||||
// TODO: Might have race condition
|
||||
rooms[roomID].rtcSessions = append(rooms[roomID].rtcSessions, webRTC)
|
||||
room := rooms[roomID]
|
||||
|
||||
webRTC.AttachRoomID(roomID)
|
||||
go startWebRTCSession(room, webRTC, playerIndex)
|
||||
|
||||
return roomID, isNewRoom
|
||||
}
|
||||
|
||||
// Session represents a session connected from the browser to the current server
|
||||
type Session struct {
|
||||
client *Client
|
||||
peerconnection *webrtc.WebRTC
|
||||
ServerID string
|
||||
}
|
||||
|
||||
// Handle normal traffic (from browser to host)
|
||||
func ws(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Print("[!] WS upgrade:", err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
var gameName string
|
||||
var roomID string
|
||||
var playerIndex int
|
||||
|
||||
// Create connection to overlord
|
||||
client := NewClient(c)
|
||||
//sessionID := strconv.Itoa(rand.Int())
|
||||
sessionID := uuid.Must(uuid.NewV4()).String()
|
||||
|
||||
wssession := &Session{
|
||||
client: client,
|
||||
peerconnection: webrtc.NewWebRTC(),
|
||||
// The server session is maintaining
|
||||
}
|
||||
|
||||
client.send(WSPacket{
|
||||
ID: "gamelist",
|
||||
Data: getEncodedGameList(),
|
||||
}, nil)
|
||||
|
||||
client.receive("heartbeat", func(resp WSPacket) WSPacket {
|
||||
return resp
|
||||
})
|
||||
|
||||
client.receive("initwebrtc", func(resp WSPacket) WSPacket {
|
||||
log.Println("Received user SDP")
|
||||
localSession, err := wssession.peerconnection.StartClient(resp.Data, width, height)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
return WSPacket{
|
||||
ID: "sdp",
|
||||
Data: localSession,
|
||||
SessionID: sessionID,
|
||||
}
|
||||
})
|
||||
|
||||
client.receive("save", func(resp WSPacket) (req WSPacket) {
|
||||
log.Println("Saving game state")
|
||||
req.ID = "save"
|
||||
req.Data = "ok"
|
||||
if roomID != "" {
|
||||
err = rooms[roomID].director.SaveGame()
|
||||
if err != nil {
|
||||
log.Println("[!] Cannot save game state: ", err)
|
||||
req.Data = "error"
|
||||
}
|
||||
} else {
|
||||
req.Data = "error"
|
||||
}
|
||||
|
||||
return req
|
||||
})
|
||||
|
||||
client.receive("load", func(resp WSPacket) (req WSPacket) {
|
||||
log.Println("Loading game state")
|
||||
req.ID = "load"
|
||||
req.Data = "ok"
|
||||
if roomID != "" {
|
||||
err = rooms[roomID].director.LoadGame()
|
||||
if err != nil {
|
||||
log.Println("[!] Cannot load game state: ", err)
|
||||
req.Data = "error"
|
||||
}
|
||||
} else {
|
||||
req.Data = "error"
|
||||
}
|
||||
|
||||
return req
|
||||
})
|
||||
|
||||
client.receive("start", func(resp WSPacket) (req WSPacket) {
|
||||
gameName = resp.Data
|
||||
roomID = resp.RoomID
|
||||
playerIndex = resp.PlayerIndex
|
||||
isNewRoom := false
|
||||
|
||||
log.Println("Starting game")
|
||||
// If we are connecting to overlord, request serverID from roomID
|
||||
if oclient != nil {
|
||||
roomServerID := getServerIDOfRoom(oclient, roomID)
|
||||
log.Println("Server of RoomID ", roomID, " is ", roomServerID)
|
||||
if roomServerID != "" && wssession.ServerID != roomServerID {
|
||||
// TODO: Re -register
|
||||
go bridgeConnection(wssession, roomServerID, gameName, roomID, playerIndex)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
roomID, isNewRoom = startSession(wssession.peerconnection, gameName, roomID, playerIndex)
|
||||
// Register room to overlord if we are connecting to overlord
|
||||
if isNewRoom && oclient != nil {
|
||||
oclient.send(WSPacket{
|
||||
ID: "registerRoom",
|
||||
Data: roomID,
|
||||
}, nil)
|
||||
}
|
||||
req.ID = "start"
|
||||
req.RoomID = roomID
|
||||
req.SessionID = sessionID
|
||||
|
||||
return req
|
||||
})
|
||||
|
||||
client.receive("candidate", func(resp WSPacket) (req WSPacket) {
|
||||
// Unuse code
|
||||
hi := pionRTC.ICECandidateInit{}
|
||||
err = json.Unmarshal([]byte(resp.Data), &hi)
|
||||
if err != nil {
|
||||
log.Println("[!] Cannot parse candidate: ", err)
|
||||
} else {
|
||||
// webRTC.AddCandidate(hi)
|
||||
}
|
||||
req.ID = "candidate"
|
||||
|
||||
return req
|
||||
})
|
||||
|
||||
client.listen()
|
||||
}
|
||||
|
||||
// generateRoomID generate a unique room ID containing 16 digits
|
||||
func generateRoomID() string {
|
||||
roomID := strconv.FormatInt(rand.Int63(), 16)
|
||||
//roomID := uuid.Must(uuid.NewV4()).String()
|
||||
return roomID
|
||||
}
|
||||
|
||||
func (r *Room) startVideo() {
|
||||
// fanout Screen
|
||||
for {
|
||||
select {
|
||||
case <-r.Done:
|
||||
r.remove()
|
||||
return
|
||||
case image := <-r.imageChannel:
|
||||
//isRoomRunning := false
|
||||
|
||||
yuv := util.RgbaToYuv(image)
|
||||
r.sessionsLock.Lock()
|
||||
for _, webRTC := range r.rtcSessions {
|
||||
// Client stopped
|
||||
if webRTC.IsClosed() {
|
||||
continue
|
||||
}
|
||||
|
||||
// encode frame
|
||||
// fanout imageChannel
|
||||
if webRTC.IsConnected() {
|
||||
// NOTE: can block here
|
||||
webRTC.ImageChannel <- yuv
|
||||
}
|
||||
//isRoomRunning = true
|
||||
}
|
||||
r.sessionsLock.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Room) startAudio() {
|
||||
log.Println("Enter fan audio")
|
||||
|
||||
enc, err := opus.NewEncoder(emulator.SampleRate, emulator.Channels, opus.AppAudio)
|
||||
|
||||
maxBufferSize := emulator.TimeFrame * emulator.SampleRate / 1000
|
||||
pcm := make([]float32, maxBufferSize) // 640 * 1000 / 16000 == 40 ms
|
||||
idx := 0
|
||||
|
||||
if err != nil {
|
||||
log.Println("[!] Cannot create audio encoder")
|
||||
return
|
||||
}
|
||||
|
||||
var count byte = 0
|
||||
|
||||
// fanout Audio
|
||||
for {
|
||||
select {
|
||||
case <-r.Done:
|
||||
r.remove()
|
||||
return
|
||||
case sample := <-r.audioChannel:
|
||||
pcm[idx] = sample
|
||||
idx++
|
||||
if idx == len(pcm) {
|
||||
data := make([]byte, 640)
|
||||
|
||||
n, err := enc.EncodeFloat32(pcm, data)
|
||||
|
||||
if err != nil {
|
||||
log.Println("[!] Failed to decode")
|
||||
continue
|
||||
}
|
||||
data = data[:n]
|
||||
data = append(data, count)
|
||||
|
||||
r.sessionsLock.Lock()
|
||||
for _, webRTC := range r.rtcSessions {
|
||||
// Client stopped
|
||||
if webRTC.IsClosed() {
|
||||
continue
|
||||
}
|
||||
|
||||
// encode frame
|
||||
// fanout audioChannel
|
||||
if webRTC.IsConnected() {
|
||||
// NOTE: can block here
|
||||
webRTC.AudioChannel <- data
|
||||
}
|
||||
//isRoomRunning = true
|
||||
}
|
||||
r.sessionsLock.Unlock()
|
||||
|
||||
idx = 0
|
||||
count = (count + 1) & 0xff
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Room) remove() {
|
||||
log.Println("Closing room", r)
|
||||
r.director.Done <- struct{}{}
|
||||
}
|
||||
|
||||
// startWebRTCSession fan-in of the same room to inputChannel
|
||||
func startWebRTCSession(room *Room, webRTC *webrtc.WebRTC, playerIndex int) {
|
||||
inputChannel := room.inputChannel
|
||||
log.Println("room, inputChannel", room, inputChannel)
|
||||
for {
|
||||
select {
|
||||
case <-webRTC.Done:
|
||||
removeSession(webRTC, room)
|
||||
default:
|
||||
}
|
||||
// Client stopped
|
||||
if webRTC.IsClosed() {
|
||||
return
|
||||
}
|
||||
|
||||
// encode frame
|
||||
if webRTC.IsConnected() {
|
||||
input := <-webRTC.InputChannel
|
||||
// the first 8 bits belong to player 1
|
||||
// the next 8 belongs to player 2 ...
|
||||
// We standardize and put it to inputChannel (16 bits)
|
||||
input = input << ((uint(playerIndex) - 1) * emulator.NumKeys)
|
||||
inputChannel <- input
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cleanSession(w *webrtc.WebRTC) {
|
||||
room, ok := rooms[w.RoomID]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
removeSession(w, room)
|
||||
}
|
||||
|
||||
func removeSession(w *webrtc.WebRTC, room *Room) {
|
||||
room.sessionsLock.Lock()
|
||||
defer room.sessionsLock.Unlock()
|
||||
for i, s := range room.rtcSessions {
|
||||
if s == w {
|
||||
room.rtcSessions = append(room.rtcSessions[:i], room.rtcSessions[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
// If room has no sessions, close room
|
||||
if len(room.rtcSessions) == 0 {
|
||||
room.Done <- struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func getServerIDOfRoom(oc *Client, roomID string) string {
|
||||
log.Println("Request overlord roomID")
|
||||
packet := oc.syncSend(
|
||||
WSPacket{
|
||||
ID: "getRoom",
|
||||
Data: roomID,
|
||||
},
|
||||
)
|
||||
log.Println("Received roomID from overlord")
|
||||
|
||||
return packet.Data
|
||||
}
|
||||
|
||||
func bridgeConnection(session *Session, serverID string, gameName string, roomID string, playerIndex int) {
|
||||
log.Println("Bridging connection to other Host ", serverID)
|
||||
client := session.client
|
||||
// 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")
|
||||
// Send back remote SDP of remote server to browser
|
||||
//client.syncSend(WSPacket{
|
||||
//ID: "sdp",
|
||||
//Data: remoteTargetSDP.Data,
|
||||
//})
|
||||
client.send(WSPacket{
|
||||
ID: "sdp",
|
||||
Data: remoteTargetSDP.Data,
|
||||
}, nil)
|
||||
log.Println("Init session done, start game on target host")
|
||||
|
||||
oclient.syncSend(WSPacket{
|
||||
ID: "start",
|
||||
Data: gameName,
|
||||
TargetHostID: serverID,
|
||||
RoomID: roomID,
|
||||
PlayerIndex: playerIndex,
|
||||
})
|
||||
log.Println("Game is started on remote host")
|
||||
}
|
||||
|
||||
func createOverlordConnection() (*websocket.Conn, error) {
|
||||
c, _, err := websocket.DefaultDialer.Dial(*config.OverlordHost, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func NewOverlordClient(oc *websocket.Conn) *Client {
|
||||
oclient := NewClient(oc)
|
||||
|
||||
// Received from overlord the serverID
|
||||
oclient.receive(
|
||||
"serverID",
|
||||
func(response WSPacket) (request WSPacket) {
|
||||
// Stick session with serverID got from overlord
|
||||
log.Println("Received serverID ", response.Data)
|
||||
serverID = response.Data
|
||||
|
||||
return EmptyPacket
|
||||
},
|
||||
)
|
||||
|
||||
// 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")
|
||||
peerconnection := webrtc.NewWebRTC()
|
||||
// init new peerconnection from sessionID
|
||||
localSession, err := peerconnection.StartClient(resp.Data, width, height)
|
||||
peerconnections[resp.SessionID] = peerconnection
|
||||
|
||||
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")
|
||||
|
||||
peerconnection := peerconnections[resp.SessionID]
|
||||
log.Println("start session")
|
||||
roomID, isNewRoom := startSession(peerconnection, resp.Data, resp.RoomID, resp.PlayerIndex)
|
||||
log.Println("Done, sending back")
|
||||
// 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
|
||||
},
|
||||
)
|
||||
// heartbeat to keep pinging overlord. We not ping from server to browser, so we don't call heartbeat in browserClient
|
||||
go oclient.heartbeat()
|
||||
go oclient.listen()
|
||||
|
||||
return oclient
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,8 +95,8 @@ func (c *Client) Receive(id string, f func(response WSPacket) (request WSPacket)
|
|||
}
|
||||
}
|
||||
|
||||
// syncSend sends a packet and wait for callback till the packet comes back
|
||||
func (c *Client) syncSend(request WSPacket) (response WSPacket) {
|
||||
// SyncSend sends a packet and wait for callback till the packet comes back
|
||||
func (c *Client) SyncSend(request WSPacket) (response WSPacket) {
|
||||
res := make(chan WSPacket)
|
||||
f := func(resp WSPacket) {
|
||||
res <- resp
|
||||
|
|
@ -105,8 +105,8 @@ func (c *Client) syncSend(request WSPacket) (response WSPacket) {
|
|||
return <-res
|
||||
}
|
||||
|
||||
// heartbeat maintains connection to server
|
||||
func (c *Client) heartbeat() {
|
||||
// Heartbeat maintains connection to server
|
||||
func (c *Client) Heartbeat() {
|
||||
// send heartbeat every 1s
|
||||
timer := time.Tick(time.Second)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package director
|
||||
package emulator
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// credit to https://github.com/fogleman/nes
|
||||
package director
|
||||
package emulator
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// credit to https://github.com/fogleman/nes
|
||||
package director
|
||||
package emulator
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
|
@ -34,6 +34,7 @@ const (
|
|||
SampleRate = 16000
|
||||
Channels = 1
|
||||
TimeFrame = 60
|
||||
AppAudio = 1
|
||||
)
|
||||
|
||||
type GameView struct {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// credit to https://github.com/fogleman/nes
|
||||
package director
|
||||
package emulator
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
|
|
|
|||
156
handler/browser.go
Normal file
156
handler/browser.go
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
"github.com/giongto35/cloud-game/config"
|
||||
"github.com/giongto35/cloud-game/cws"
|
||||
"github.com/giongto35/cloud-game/handler/gamelist"
|
||||
"github.com/giongto35/cloud-game/webrtc"
|
||||
"github.com/gorilla/websocket"
|
||||
pionRTC "github.com/pion/webrtc"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
type BrowserClient struct {
|
||||
*cws.Client
|
||||
session *Session
|
||||
oclient *OverlordClient
|
||||
gameName string
|
||||
roomID string
|
||||
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)
|
||||
|
||||
browserClient.Receive("heartbeat", func(resp cws.WSPacket) cws.WSPacket {
|
||||
return resp
|
||||
})
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
return cws.WSPacket{
|
||||
ID: "sdp",
|
||||
Data: localSession,
|
||||
SessionID: sessionID,
|
||||
}
|
||||
})
|
||||
|
||||
// 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 roomID != "" {
|
||||
////err := rooms[roomID].director.SaveGame()
|
||||
//err := browserClient.room.director.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 roomID != "" {
|
||||
//err := rooms[roomID].director.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) {
|
||||
gameName = resp.Data
|
||||
roomID = resp.RoomID
|
||||
playerIndex = resp.PlayerIndex
|
||||
isNewRoom := false
|
||||
|
||||
log.Println("Starting game")
|
||||
// If we are connecting to overlord, request serverID from roomID
|
||||
if browserClient.oclient != nil {
|
||||
session := browserClient.session
|
||||
roomServerID := getServerIDOfRoom(session.OverlordClient, roomID)
|
||||
log.Println("Server of RoomID ", roomID, " is ", roomServerID)
|
||||
if roomServerID != "" && wssession.ServerID != roomServerID {
|
||||
// TODO: Re -register
|
||||
go bridgeConnection(wssession, roomServerID, gameName, roomID, playerIndex)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
roomID, isNewRoom = startSession(wssession.peerconnection, gameName, roomID, playerIndex)
|
||||
// Register room to overlord if we are connecting to overlord
|
||||
if isNewRoom && browserClient.session.OverlordClient != nil {
|
||||
browserClient.session.OverlordClient.Send(cws.WSPacket{
|
||||
ID: "registerRoom",
|
||||
Data: roomID,
|
||||
}, nil)
|
||||
}
|
||||
req.ID = "start"
|
||||
req.RoomID = roomID
|
||||
req.SessionID = sessionID
|
||||
|
||||
return req
|
||||
})
|
||||
|
||||
browserClient.Receive("candidate", func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
// Unuse code
|
||||
hi := pionRTC.ICECandidateInit{}
|
||||
err := json.Unmarshal([]byte(resp.Data), &hi)
|
||||
if err != nil {
|
||||
log.Println("[!] Cannot parse candidate: ", err)
|
||||
} else {
|
||||
// webRTC.AddCandidate(hi)
|
||||
}
|
||||
req.ID = "candidate"
|
||||
|
||||
return req
|
||||
})
|
||||
|
||||
return browserClient
|
||||
}
|
||||
|
|
@ -7,14 +7,11 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/giongto35/cloud-game/config"
|
||||
"github.com/giongto35/cloud-game/cws"
|
||||
"github.com/giongto35/cloud-game/handlers/client"
|
||||
"github.com/giongto35/cloud-game/webrtc"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
const (
|
||||
width = 256
|
||||
height = 240
|
||||
scale = 3
|
||||
title = "NES"
|
||||
gameboyIndex = "./static/gameboy.html"
|
||||
|
|
@ -33,11 +30,30 @@ var upgrader = websocket.Upgrader{}
|
|||
|
||||
// ID to peerconnection
|
||||
//var peerconnections = map[string]*webrtc.WebRTC{}
|
||||
var serverID = ""
|
||||
var oclient *cws.Client
|
||||
var oclient *OverlordClient
|
||||
|
||||
// getWeb returns web frontend
|
||||
func getWeb(w http.ResponseWriter, r *http.Request) {
|
||||
type Handler struct {
|
||||
oClient *OverlordClient
|
||||
rooms map[string]*Room
|
||||
serverID string
|
||||
// ID to peerconnection
|
||||
peerconnections map[string]*webrtc.WebRTC
|
||||
}
|
||||
|
||||
func NewHandler() (*Handler, error) {
|
||||
conn, err := createOverlordConnection()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Handler{
|
||||
oClient: NewOverlordClient(conn),
|
||||
rooms: map[string]*Room{},
|
||||
peerconnections: map[string]*webrtc.WebRTC{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetWeb returns web frontend
|
||||
func (h *Handler) GetWeb(w http.ResponseWriter, r *http.Request) {
|
||||
bs, err := ioutil.ReadFile(indexFN)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
@ -46,7 +62,7 @@ func getWeb(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// Handle normal traffic (from browser to host)
|
||||
func ws(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handler) WS(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Print("[!] WS upgrade:", err)
|
||||
|
|
@ -172,58 +188,8 @@ func ws(w http.ResponseWriter, r *http.Request) {
|
|||
//return req
|
||||
//})
|
||||
|
||||
client := client.NewBrowserClient(c)
|
||||
client.listen()
|
||||
}
|
||||
|
||||
func getServerIDOfRoom(oc *Client, roomID string) string {
|
||||
log.Println("Request overlord roomID")
|
||||
packet := oc.syncSend(
|
||||
cws.WSPacket{
|
||||
ID: "getRoom",
|
||||
Data: roomID,
|
||||
},
|
||||
)
|
||||
log.Println("Received roomID from overlord")
|
||||
|
||||
return packet.Data
|
||||
}
|
||||
|
||||
func bridgeConnection(session *Session, serverID string, gameName string, roomID string, playerIndex int) {
|
||||
log.Println("Bridging connection to other Host ", serverID)
|
||||
client := session.client
|
||||
// Ask client to init
|
||||
|
||||
log.Println("Requesting offer to browser", serverID)
|
||||
resp := client.syncSend(cws.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")
|
||||
// Send back remote SDP of remote server to browser
|
||||
//client.syncSend(WSPacket{
|
||||
//ID: "sdp",
|
||||
//Data: remoteTargetSDP.Data,
|
||||
//})
|
||||
client.send(cws.WSPacket{
|
||||
ID: "sdp",
|
||||
Data: remoteTargetSDP.Data,
|
||||
}, nil)
|
||||
log.Println("Init session done, start game on target host")
|
||||
|
||||
oclient.syncSend(cws.WSPacket{
|
||||
ID: "start",
|
||||
Data: gameName,
|
||||
TargetHostID: serverID,
|
||||
RoomID: roomID,
|
||||
PlayerIndex: playerIndex,
|
||||
})
|
||||
log.Println("Game is started on remote host")
|
||||
client := NewBrowserClient(c, oclient)
|
||||
client.Listen()
|
||||
}
|
||||
|
||||
func createOverlordConnection() (*websocket.Conn, error) {
|
||||
|
|
@ -3,6 +3,7 @@ package handler
|
|||
import (
|
||||
"log"
|
||||
|
||||
"github.com/giongto35/cloud-game/emulator"
|
||||
"github.com/giongto35/cloud-game/util"
|
||||
"gopkg.in/hraban/opus.v2"
|
||||
)
|
||||
|
|
@ -10,7 +11,7 @@ import (
|
|||
func (r *Room) startAudio() {
|
||||
log.Println("Enter fan audio")
|
||||
|
||||
enc, err := opus.NewEncoder(emulator.SampleRate, emulator.Channels, emulator.AppAudio)
|
||||
enc, err := opus.NewEncoder(emulator.SampleRate, emulator.Channels, opus.AppAudio)
|
||||
|
||||
maxBufferSize := emulator.TimeFrame * emulator.SampleRate / 1000
|
||||
pcm := make([]float32, maxBufferSize) // 640 * 1000 / 16000 == 40 ms
|
||||
138
handler/overlord.go
Normal file
138
handler/overlord.go
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
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"
|
||||
)
|
||||
|
||||
type OverlordClient struct {
|
||||
*cws.Client
|
||||
}
|
||||
|
||||
// NewOverlordClient returns a client connecting to overlord for coordiation between different server
|
||||
func NewOverlordClient(oc *websocket.Conn) *OverlordClient {
|
||||
oclient := &OverlordClient{
|
||||
Client: cws.NewClient(oc),
|
||||
}
|
||||
return oclient
|
||||
}
|
||||
|
||||
func (h *Handler) Register(oclient *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)
|
||||
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")
|
||||
peerconnection := webrtc.NewWebRTC()
|
||||
// init new peerconnection from sessionID
|
||||
localSession, err := peerconnection.StartClient(resp.Data, config.Width, config.Height)
|
||||
h.peerconnections[resp.SessionID] = peerconnection
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
return cws.WSPacket{
|
||||
ID: "sdp",
|
||||
Data: localSession,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Received start from overlord. This is 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")
|
||||
|
||||
peerconnection := h.peerconnections[resp.SessionID]
|
||||
log.Println("start session")
|
||||
roomID, isNewRoom := startSession(peerconnection, resp.Data, resp.RoomID, resp.PlayerIndex)
|
||||
log.Println("Done, sending back")
|
||||
// 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
|
||||
},
|
||||
)
|
||||
// heartbeat to keep pinging overlord. We not ping from server to browser, so we don't call heartbeat in browserClient
|
||||
go oclient.Heartbeat()
|
||||
go oclient.Listen()
|
||||
|
||||
}
|
||||
|
||||
func getServerIDOfRoom(oc *OverlordClient, roomID string) string {
|
||||
log.Println("Request overlord roomID")
|
||||
packet := oc.SyncSend(
|
||||
cws.WSPacket{
|
||||
ID: "getRoom",
|
||||
Data: roomID,
|
||||
},
|
||||
)
|
||||
log.Println("Received roomID from overlord")
|
||||
|
||||
return packet.Data
|
||||
}
|
||||
|
||||
func bridgeConnection(session *Session, serverID string, gameName string, roomID string, playerIndex int) {
|
||||
log.Println("Bridging connection to other Host ", serverID)
|
||||
client := session.BrowserClient
|
||||
// Ask client to init
|
||||
|
||||
log.Println("Requesting offer to browser", serverID)
|
||||
resp := client.SyncSend(cws.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 := session.OverlordClient.SyncSend(resp)
|
||||
log.Println("Got back remote host SDP, sending to browser")
|
||||
// Send back remote SDP of remote server to browser
|
||||
//client.syncSend(WSPacket{
|
||||
//ID: "sdp",
|
||||
//Data: remoteTargetSDP.Data,
|
||||
//})
|
||||
session.BrowserClient.Send(cws.WSPacket{
|
||||
ID: "sdp",
|
||||
Data: remoteTargetSDP.Data,
|
||||
}, nil)
|
||||
log.Println("Init session done, start game on target host")
|
||||
|
||||
session.OverlordClient.SyncSend(cws.WSPacket{
|
||||
ID: "start",
|
||||
Data: gameName,
|
||||
TargetHostID: serverID,
|
||||
RoomID: roomID,
|
||||
PlayerIndex: playerIndex,
|
||||
})
|
||||
log.Println("Game is started on remote host")
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"strconv"
|
||||
"sync"
|
||||
|
||||
ui "github.com/giongto35/cloud-game/emulator"
|
||||
emulator "github.com/giongto35/cloud-game/emulator"
|
||||
"github.com/giongto35/cloud-game/webrtc"
|
||||
)
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ type Room struct {
|
|||
rtcSessions []*webrtc.WebRTC
|
||||
sessionsLock *sync.Mutex
|
||||
|
||||
director *ui.Director
|
||||
director *emulator.Director
|
||||
}
|
||||
|
||||
var rooms = map[string]*Room{}
|
||||
|
|
@ -43,11 +43,11 @@ func initRoom(roomID, gameName string) string {
|
|||
}
|
||||
log.Println("Init new room", roomID, gameName)
|
||||
imageChannel := make(chan *image.RGBA, 100)
|
||||
audioChannel := make(chan float32, ui.SampleRate)
|
||||
audioChannel := make(chan float32, emulator.SampleRate)
|
||||
inputChannel := make(chan int, 100)
|
||||
|
||||
// create director
|
||||
director := ui.NewDirector(roomID, imageChannel, audioChannel, inputChannel)
|
||||
director := emulator.NewDirector(roomID, imageChannel, audioChannel, inputChannel)
|
||||
|
||||
room := &Room{
|
||||
imageChannel: imageChannel,
|
||||
|
|
@ -105,7 +105,7 @@ func startWebRTCSession(room *Room, webRTC *webrtc.WebRTC, playerIndex int) {
|
|||
// the first 8 bits belong to player 1
|
||||
// the next 8 belongs to player 2 ...
|
||||
// We standardize and put it to inputChannel (16 bits)
|
||||
input = input << ((uint(playerIndex) - 1) * ui.NumKeys)
|
||||
input = input << ((uint(playerIndex) - 1) * emulator.NumKeys)
|
||||
inputChannel <- input
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package client
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/giongto35/cloud-game/cws"
|
||||
"github.com/giongto35/cloud-game/webrtc"
|
||||
)
|
||||
|
||||
|
|
@ -9,7 +8,8 @@ import (
|
|||
// It involves one connection to browser and one connection to the overlord
|
||||
// Peerconnection can be from other server to ensure better latency
|
||||
type Session struct {
|
||||
client *cws.Client
|
||||
BrowserClient *BrowserClient
|
||||
OverlordClient *OverlordClient
|
||||
peerconnection *webrtc.WebRTC
|
||||
ServerID string
|
||||
}
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
"github.com/giongto35/cloud-game/config"
|
||||
"github.com/giongto35/cloud-game/cws"
|
||||
"github.com/giongto35/cloud-game/handlers/gamelist"
|
||||
"github.com/giongto35/cloud-game/webrtc"
|
||||
"github.com/gorilla/websocket"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
var rooms = map[string]*Room{}
|
||||
|
||||
// NewOverlordClient returns a client connecting to browser. This connection exchanges information between clients and server
|
||||
func NewBrowserClient(c *websocket.Conn) *cws.Client {
|
||||
roomID := ""
|
||||
// Create connection to overlord
|
||||
client := cws.NewClient(c)
|
||||
//sessionID := strconv.Itoa(rand.Int())
|
||||
sessionID := uuid.Must(uuid.NewV4()).String()
|
||||
|
||||
wssession := &Session{
|
||||
client: client,
|
||||
peerconnection: webrtc.NewWebRTC(),
|
||||
// The server session is maintaining
|
||||
}
|
||||
|
||||
client.Send(cws.WSPacket{
|
||||
ID: "gamelist",
|
||||
Data: gamelist.GetEncodedGameList(),
|
||||
}, nil)
|
||||
|
||||
client.Receive("heartbeat", func(resp cws.WSPacket) cws.WSPacket {
|
||||
return resp
|
||||
})
|
||||
|
||||
client.Receive("initwebrtc", func(resp cws.WSPacket) cws.WSPacket {
|
||||
log.Println("Received user SDP")
|
||||
localSession, err := wssession.peerconnection.StartClient(resp.Data, config.Width, config.Height)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
return cws.WSPacket{
|
||||
ID: "sdp",
|
||||
Data: localSession,
|
||||
SessionID: sessionID,
|
||||
}
|
||||
})
|
||||
|
||||
client.Receive("save", func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
log.Println("Saving game state")
|
||||
req.ID = "save"
|
||||
req.Data = "ok"
|
||||
if roomID != "" {
|
||||
err := rooms[roomID].director.SaveGame()
|
||||
if err != nil {
|
||||
log.Println("[!] Cannot save game state: ", err)
|
||||
req.Data = "error"
|
||||
}
|
||||
} else {
|
||||
req.Data = "error"
|
||||
}
|
||||
|
||||
return req
|
||||
})
|
||||
|
||||
client.Receive("load", func(resp WSPacket) (req WSPacket) {
|
||||
log.Println("Loading game state")
|
||||
req.ID = "load"
|
||||
req.Data = "ok"
|
||||
if roomID != "" {
|
||||
err = rooms[roomID].director.LoadGame()
|
||||
if err != nil {
|
||||
log.Println("[!] Cannot load game state: ", err)
|
||||
req.Data = "error"
|
||||
}
|
||||
} else {
|
||||
req.Data = "error"
|
||||
}
|
||||
|
||||
return req
|
||||
})
|
||||
|
||||
client.Receive("start", func(resp WSPacket) (req WSPacket) {
|
||||
gameName = resp.Data
|
||||
roomID = resp.RoomID
|
||||
playerIndex = resp.PlayerIndex
|
||||
isNewRoom := false
|
||||
|
||||
log.Println("Starting game")
|
||||
// If we are connecting to overlord, request serverID from roomID
|
||||
if oclient != nil {
|
||||
roomServerID := getServerIDOfRoom(oclient, roomID)
|
||||
log.Println("Server of RoomID ", roomID, " is ", roomServerID)
|
||||
if roomServerID != "" && wssession.ServerID != roomServerID {
|
||||
// TODO: Re -register
|
||||
go bridgeConnection(wssession, roomServerID, gameName, roomID, playerIndex)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
roomID, isNewRoom = startSession(wssession.peerconnection, gameName, roomID, playerIndex)
|
||||
// Register room to overlord if we are connecting to overlord
|
||||
if isNewRoom && oclient != nil {
|
||||
oclient.send(WSPacket{
|
||||
ID: "registerRoom",
|
||||
Data: roomID,
|
||||
}, nil)
|
||||
}
|
||||
req.ID = "start"
|
||||
req.RoomID = roomID
|
||||
req.SessionID = sessionID
|
||||
|
||||
return req
|
||||
})
|
||||
|
||||
client.Receive("candidate", func(resp cws.WSPacket) (req cws.WSPacket) {
|
||||
// Unuse code
|
||||
hi := pionRTC.ICECandidateInit{}
|
||||
err = json.Unmarshal([]byte(resp.Data), &hi)
|
||||
if err != nil {
|
||||
log.Println("[!] Cannot parse candidate: ", err)
|
||||
} else {
|
||||
// webRTC.AddCandidate(hi)
|
||||
}
|
||||
req.ID = "candidate"
|
||||
|
||||
return req
|
||||
})
|
||||
|
||||
return client
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/giongto35/cloud-game/cws"
|
||||
"github.com/giongto35/cloud-game/webrtc"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
// NewOverlordClient returns a client connecting to overlord for coordiation between different server
|
||||
func NewOverlordClient(oc *websocket.Conn) *cws.Client {
|
||||
oclient := NewClient(oc)
|
||||
|
||||
// Received from overlord the serverID
|
||||
oclient.receive(
|
||||
"serverID",
|
||||
func(response WSPacket) (request WSPacket) {
|
||||
// Stick session with serverID got from overlord
|
||||
log.Println("Received serverID ", response.Data)
|
||||
serverID = response.Data
|
||||
|
||||
return EmptyPacket
|
||||
},
|
||||
)
|
||||
|
||||
// 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")
|
||||
peerconnection := webrtc.NewWebRTC()
|
||||
// init new peerconnection from sessionID
|
||||
localSession, err := peerconnection.StartClient(resp.Data, width, height)
|
||||
peerconnections[resp.SessionID] = peerconnection
|
||||
|
||||
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")
|
||||
|
||||
peerconnection := peerconnections[resp.SessionID]
|
||||
log.Println("start session")
|
||||
roomID, isNewRoom := startSession(peerconnection, resp.Data, resp.RoomID, resp.PlayerIndex)
|
||||
log.Println("Done, sending back")
|
||||
// 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
|
||||
},
|
||||
)
|
||||
// heartbeat to keep pinging overlord. We not ping from server to browser, so we don't call heartbeat in browserClient
|
||||
go oclient.heartbeat()
|
||||
go oclient.listen()
|
||||
|
||||
return oclient
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/giongto35/cloud-game/config"
|
||||
"github.com/giongto35/cloud-game/webrtc"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
func createOverlordConnection() (*websocket.Conn, error) {
|
||||
c, _, err := websocket.DefaultDialer.Dial(*config.OverlordHost, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func NewOverlordClient(oc *websocket.Conn) *Client {
|
||||
oclient := NewClient(oc)
|
||||
|
||||
// Received from overlord the serverID
|
||||
oclient.receive(
|
||||
"serverID",
|
||||
func(response WSPacket) (request WSPacket) {
|
||||
// Stick session with serverID got from overlord
|
||||
log.Println("Received serverID ", response.Data)
|
||||
serverID = response.Data
|
||||
|
||||
return EmptyPacket
|
||||
},
|
||||
)
|
||||
|
||||
// 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")
|
||||
peerconnection := webrtc.NewWebRTC()
|
||||
// init new peerconnection from sessionID
|
||||
localSession, err := peerconnection.StartClient(resp.Data, width, height)
|
||||
peerconnections[resp.SessionID] = peerconnection
|
||||
|
||||
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")
|
||||
|
||||
peerconnection := peerconnections[resp.SessionID]
|
||||
log.Println("start session")
|
||||
roomID, isNewRoom := startSession(peerconnection, resp.Data, resp.RoomID, resp.PlayerIndex)
|
||||
log.Println("Done, sending back")
|
||||
// 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
|
||||
},
|
||||
)
|
||||
// heartbeat to keep pinging overlord. We not ping from server to browser, so we don't call heartbeat in browserClient
|
||||
go oclient.heartbeat()
|
||||
go oclient.listen()
|
||||
|
||||
return oclient
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package overlord
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -7,16 +7,17 @@ import (
|
|||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/giongto35/cloud-game/cws"
|
||||
"github.com/giongto35/cloud-game/webrtc"
|
||||
)
|
||||
|
||||
var roomToServer = map[string]string{}
|
||||
|
||||
// servers are the map serverID to server Client
|
||||
var servers = map[string]*Client{}
|
||||
var servers = map[string]*cws.Client{}
|
||||
|
||||
// If it's overlord, handle overlord connection (from host to overlord)
|
||||
func wso(w http.ResponseWriter, r *http.Request) {
|
||||
func WSO(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("Connected")
|
||||
c, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
|
|
@ -119,5 +120,5 @@ func wso(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
})
|
||||
|
||||
client.listen()
|
||||
client.listeồi
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue