This commit is contained in:
Nguyen Huu Thanh 2019-05-01 11:02:37 +08:00
parent 81cdf27acc
commit dd415f1cce
6 changed files with 191 additions and 142 deletions

View file

@ -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)
}
}

View file

@ -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) {

View file

@ -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
}

View file

@ -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 {

View file

@ -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(

View file

@ -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()
}