mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-07-21 09:08:57 +00:00
Merge with master
This commit is contained in:
commit
6bf2db51ac
11 changed files with 61 additions and 56 deletions
1
Dockerfile
vendored
1
Dockerfile
vendored
|
|
@ -5,7 +5,6 @@ RUN apt-get update
|
|||
RUN apt-get install pkg-config libvpx-dev libopus-dev libopusfile-dev -y
|
||||
|
||||
RUN mkdir -p /cloud-game
|
||||
VOLUME /home/thanh/Desktop/Code/cloud-game/games:/cloud-game/games
|
||||
COPY . /cloud-game/
|
||||
WORKDIR /cloud-game
|
||||
|
||||
|
|
|
|||
23
Makefile
vendored
23
Makefile
vendored
|
|
@ -83,26 +83,3 @@ dev.run-docker:
|
|||
docker rm cloud-game-local
|
||||
# Overlord and worker should be run separately.
|
||||
docker run --privileged -v $PWD/games:/cloud-game/games -d --name cloud-game-local -p 8000:8000 -p 9000:9000 cloud-game-local bash -c "overlord --v=5 & overworker --overlordhost ws://localhost:8000/wso"
|
||||
|
||||
#build:
|
||||
# go build -o build/cloudretro ./cmd
|
||||
#
|
||||
#run: build
|
||||
# # Run coordinator first
|
||||
# ./build/cloudretro -overlordhost overlord &
|
||||
# # Wait till overlord finish initialized
|
||||
# # Run a worker connecting to overload
|
||||
# ./build/cloudretro -overlordhost ws://localhost:8000/wso
|
||||
|
||||
#run-docker:
|
||||
# docker build . -t cloud-game-local
|
||||
# docker stop cloud-game-local || true
|
||||
# docker rm cloud-game-local || true
|
||||
# # Overlord and worker should be run separately. Local is for demo purpose
|
||||
# docker run --privileged -v $(PWD)/games:/cloud-game/games -d --name cloud-game-local -p 8000:8000 -p 9000:9000 cloud-game-local bash -c "cmd -overlordhost ws://localhost:8000/wso & cmd -overlordhost overlord"
|
||||
#
|
||||
|
||||
#run-fast: build-vendor
|
||||
# ./build/cloudretro -overlordhost overlord &
|
||||
# ./build/cloudretro -overlordhost ws://localhost:8000/wso
|
||||
#
|
||||
|
|
|
|||
10
README.md
vendored
10
README.md
vendored
|
|
@ -3,13 +3,13 @@
|
|||
**Video demo**: https://www.youtube.com/watch?v=GUBrJGAxZZg
|
||||
|
||||
## Introduction
|
||||
This project aims to experiment Cloud-gaming performance with [WebRTC](https://github.com/pion/webrtc/) and [libretro](https://www.libretro.com/), as well as trying to deliver the most modern and convenient gaming experience through the technology. Theoretically, games are run on remote servers and media are streamed to the player in an optimal way to ensure the most comfortable user interaction. It opens the ability to play any retro games on web-browser directly, which are fully compatible with multi-platform like Desktop, Android, ~~IOS~~. This flexibility also enables online gaming experience to retro games.
|
||||
This project aims to experiment Cloud-gaming performance with [WebRTC](https://github.com/pion/webrtc/) and [libretro](https://www.libretro.com/), as well as trying to deliver the most modern and convenient gaming experience through the technology. Theoretically, games are run on remote servers and media are streamed to the player optimally to ensure the most comfortable user interaction. It opens the ability to play any retro games on web-browser directly, which are fully compatible with multi-platform like Desktop, Android, ~~IOS~~. This flexibility also enables online gaming experience to retro games.
|
||||
|
||||
## Try the service at
|
||||
**[http://cloudretro.io](http://cloudretro.io)**
|
||||
*Chrome and Chrome on Android is recommended. It's not working on Iphone and some other explorers. Click help button in the game UI to see keyboard mapping.*
|
||||
*Chrome and Chrome on Android is recommended. It's not working on iPhone and some other explorers. Click help button in the game UI to see keyboard mapping.*
|
||||
|
||||
\*In ideal network condition and less resource contention on servers, the game will run smoothly as in the video demo. Because I only hosted the platform on a limited servers in US East, US West, Eu, Singapore, you may experience some latency issues + connection problem. Please retry later in less peak hours. You can try hosting your own service following the instruction the next section to have a better sense of performance.
|
||||
\*In ideal network condition and less resource contention on servers, the game will run smoothly as in the video demo. Because I only hosted the platform on limited servers in US East, US West, Eu, Singapore, you may experience some latency issues + connection problem. You can try hosting the service following the instruction the next section to have a better sense of performance.
|
||||
|
||||
Screenshot | Screenshot
|
||||
:-------------------------:|:-------------------------:
|
||||
|
|
@ -47,10 +47,10 @@ brew install libvpx pkg-config opus opusfile
|
|||
... not tested yet ...
|
||||
```
|
||||
|
||||
Because coordinator and workers needs to run simulateneously. Workers connects to coordinator.
|
||||
Because the coordinator and workers need to run simultaneously. Workers connect to the coordinator.
|
||||
1. Script
|
||||
* `make dev.run`
|
||||
* The scripts spawns 2 processes one in background and one in foreground
|
||||
* The scripts spawns 2 processes one in the background and one in foreground
|
||||
2. Manual
|
||||
* Need to run coordinator and worker separately in two session
|
||||
* `go run cmd/overlord/main.go` - spawn coordinator
|
||||
|
|
|
|||
2
go.mod
vendored
2
go.mod
vendored
|
|
@ -9,6 +9,8 @@ require (
|
|||
github.com/gofrs/uuid v3.2.0+incompatible
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
|
||||
github.com/gorilla/websocket v1.4.0
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/pion/webrtc/v2 v2.1.2
|
||||
github.com/prometheus/client_golang v1.1.0
|
||||
github.com/spf13/pflag v1.0.3
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@ const DefaultSTUNTURN = `[{"urls":"stun:stun-turn.webgame2d.com:3478"},{"urls":"
|
|||
const CODEC_VP8 = "VP8"
|
||||
const CODEC_H264 = "H264"
|
||||
|
||||
var IsDebug = flag.Bool("debug", false, "Is game running in debug mode?")
|
||||
var Port = flag.String("port", "8000", "Port of the game")
|
||||
var IsMonitor = flag.Bool("monitor", false, "Turn on monitor")
|
||||
var FrontendSTUNTURN = flag.String("stunturn", DefaultSTUNTURN, "Frontend STUN TURN servers")
|
||||
var Mode = flag.String("mode", "dev", "Environment")
|
||||
var StunTurnTemplate = `[{"urls":"stun:stun.l.google.com:19302"},{"urls":"stun:%s:3478"},{"urls":"turn:%s:3478","username":"root","credential":"root"}]`
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ func coreEnvironment(cmd C.unsigned, data unsafe.Pointer) C.bool {
|
|||
return videoSetPixelFormat(*format)
|
||||
case C.RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY:
|
||||
path := (**C.char)(data)
|
||||
*path = C.CString("./libretro/system")
|
||||
*path = C.CString("./pkg/emulator/libretro/system")
|
||||
return true
|
||||
case C.RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY:
|
||||
path := (**C.char)(data)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
type Config struct {
|
||||
Port int
|
||||
URLPrefix string
|
||||
DebugHost string
|
||||
|
||||
MonitoringConfig monitoring.ServerMonitoringConfig
|
||||
}
|
||||
|
|
@ -31,6 +32,7 @@ func (c *Config) AddFlags(fs *pflag.FlagSet) *Config {
|
|||
fs.BoolVarP(&c.MonitoringConfig.ProfilingEnabled, "monitoring.pprof", "p", c.MonitoringConfig.ProfilingEnabled, "Enable golang pprof for server")
|
||||
fs.IntVarP(&c.MonitoringConfig.Port, "monitoring.port", "", c.MonitoringConfig.Port, "Monitoring server port")
|
||||
fs.StringVarP(&c.MonitoringConfig.URLPrefix, "monitoring.prefix", "", c.MonitoringConfig.URLPrefix, "Monitoring server url prefix")
|
||||
fs.StringVarP(&c.DebugHost, "debughost", "d", "", "Specify the server want to connect directly to debug")
|
||||
|
||||
return c
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ const (
|
|||
)
|
||||
|
||||
type Server struct {
|
||||
cfg Config
|
||||
// roomToServer map roomID to workerID
|
||||
roomToServer map[string]string
|
||||
// workerClients are the map serverID to worker Client
|
||||
|
|
@ -33,8 +34,9 @@ type Server struct {
|
|||
var upgrader = websocket.Upgrader{}
|
||||
var errNotFound = errors.New("Not found")
|
||||
|
||||
func NewServer() *Server {
|
||||
func NewServer(cfg Config) *Server {
|
||||
return &Server{
|
||||
cfg: cfg,
|
||||
// Mapping serverID to client
|
||||
workerClients: map[string]*WorkerClient{},
|
||||
// Mapping roomID to server
|
||||
|
|
@ -127,30 +129,21 @@ func (o *Server) WS(w http.ResponseWriter, r *http.Request) {
|
|||
client := NewBrowserClient(c)
|
||||
go client.Listen()
|
||||
|
||||
// Set up server
|
||||
|
||||
workerClients := o.getAvailableWorkers()
|
||||
// SessionID will be the unique per frontend connection
|
||||
sessionID := uuid.Must(uuid.NewV4()).String()
|
||||
var serverID string
|
||||
if config.MatchWorkerRandom {
|
||||
serverID, err = findBestServerRandom(workerClients)
|
||||
} else {
|
||||
serverID, err = findBestServerFromBrowser(workerClients, client)
|
||||
}
|
||||
|
||||
// Get best server for frontend to connect to
|
||||
workerClient, err := o.getBestWorkerClient(client)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// SessionID will be the unique per frontend connection
|
||||
sessionID := uuid.Must(uuid.NewV4()).String()
|
||||
// Setup session
|
||||
wssession := &Session{
|
||||
ID: sessionID,
|
||||
handler: o,
|
||||
BrowserClient: client,
|
||||
WorkerClient: o.workerClients[serverID],
|
||||
ServerID: serverID,
|
||||
WorkerClient: workerClient,
|
||||
ServerID: workerClient.ServerID,
|
||||
}
|
||||
// TODO:?
|
||||
// defer wssession.Close()
|
||||
|
|
@ -161,7 +154,7 @@ func (o *Server) WS(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
wssession.BrowserClient.Send(cws.WSPacket{
|
||||
ID: "init",
|
||||
Data: createInitPackage(o.workerClients[serverID].StunTurnServer),
|
||||
Data: createInitPackage(workerClient.StunTurnServer),
|
||||
}, nil)
|
||||
|
||||
// If peerconnection is done (client.Done is signalled), we close peerconnection
|
||||
|
|
@ -178,6 +171,35 @@ func (o *Server) WS(w http.ResponseWriter, r *http.Request) {
|
|||
wssession.WorkerClient.IsAvailable = true
|
||||
}
|
||||
|
||||
func (o *Server) getBestWorkerClient(client *BrowserClient) (*WorkerClient, error) {
|
||||
if o.cfg.DebugHost != "" {
|
||||
log.Println("Connecting to debug host instead prod servers", o.cfg.DebugHost)
|
||||
wc := o.getWorkerFromAddress(o.cfg.DebugHost)
|
||||
if wc != nil {
|
||||
return wc, nil
|
||||
}
|
||||
// if there is not debugHost, continue usual flow
|
||||
log.Println("Not found, connecting to all available servers")
|
||||
}
|
||||
|
||||
workerClients := o.getAvailableWorkers()
|
||||
|
||||
var serverID string
|
||||
var err error
|
||||
if config.MatchWorkerRandom {
|
||||
serverID, err = findBestServerRandom(workerClients)
|
||||
} else {
|
||||
serverID, err = findBestServerFromBrowser(workerClients, client)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return o.workerClients[serverID], nil
|
||||
}
|
||||
|
||||
// getAvailableWorkers returns the list of available worker
|
||||
func (o *Server) getAvailableWorkers() map[string]*WorkerClient {
|
||||
workerClients := map[string]*WorkerClient{}
|
||||
|
|
@ -190,6 +212,17 @@ func (o *Server) getAvailableWorkers() map[string]*WorkerClient {
|
|||
return workerClients
|
||||
}
|
||||
|
||||
// getWorkerFromAddress returns the worker has given address
|
||||
func (o *Server) getWorkerFromAddress(address string) *WorkerClient {
|
||||
for _, w := range o.workerClients {
|
||||
if w.IsAvailable && w.Address == address {
|
||||
return w
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// findBestServer returns the best server for a session
|
||||
func findBestServerRandom(workerClients map[string]*WorkerClient) (string, error) {
|
||||
// TODO: Find best Server by latency, currently return by ping
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ func (o *Overlord) Shutdown() {
|
|||
|
||||
// initializeOverlord setup an overlord server
|
||||
func (o *Overlord) initializeOverlord() {
|
||||
overlord := NewServer()
|
||||
overlord := NewServer(o.cfg)
|
||||
|
||||
http.HandleFunc("/", overlord.GetWeb)
|
||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./web"))))
|
||||
|
|
|
|||
|
|
@ -271,9 +271,6 @@ func (w *WebRTC) startStreaming(vp8Track *webrtc.Track, opusTrack *webrtc.Track)
|
|||
}()
|
||||
|
||||
for data := range w.ImageChannel {
|
||||
if *config.IsMonitor {
|
||||
log.Println("Encoding FPS : ", w.calculateFPS())
|
||||
}
|
||||
err := vp8Track.WriteSample(media.Sample{Data: data, Samples: 1})
|
||||
if err != nil {
|
||||
log.Println("Warn: Err write sample: ", err)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package worker
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/giongto35/cloud-game/pkg/cws"
|
||||
|
|
@ -57,12 +56,10 @@ func (h *Handler) RouteOverlord() {
|
|||
SDP string `json:"sdp"`
|
||||
IsMobile bool `json:"is_mobile"`
|
||||
}
|
||||
fmt.Println("HIHIHIHI!!!!", resp.Data)
|
||||
err := json.Unmarshal([]byte(resp.Data), &initPacket)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("HIHIHIHI!!!!", initPacket)
|
||||
localSession, err := peerconnection.StartClient(initPacket.SDP, initPacket.IsMobile, iceCandidates[resp.SessionID])
|
||||
// h.peerconnections[resp.SessionID] = peerconnection
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue