From 65996587343089bf7e688f71bd337df301121671 Mon Sep 17 00:00:00 2001 From: exel Date: Fri, 5 Apr 2019 14:08:06 +0800 Subject: [PATCH] multi game session --- index.html | 111 +++++++++++++++++++++++++---------------------- main.go | 39 ++++++++++++----- ui/director.go | 10 ++++- ui/run.go | 5 ++- webrtc/webrtc.go | 7 +++ 5 files changed, 107 insertions(+), 65 deletions(-) diff --git a/index.html b/index.html index 582263fb..5440435a 100644 --- a/index.html +++ b/index.html @@ -10,9 +10,10 @@ textarea {

Use Up, Down, Left, Right to Move
- Space to jump
- Ctrl to sprint
- Enter to select in menu
+ Z to jump (A)
+ X to sprint (B)
+ C is start button
+ V is select button
Fullscreen media for better gaming experience
@@ -76,7 +77,6 @@ pc.ontrack = function (event) { document.getElementById('remoteVideos').appendChild(el) } -pc.oniceconnectionstatechange = e => log(pc.iceConnectionState) pc.onicecandidate = event => { if (event.candidate === null) { var session = btoa(JSON.stringify(pc.localDescription)); @@ -115,7 +115,7 @@ keyMap = { } INPUT_FPS = 100; -INPUT_STATE_PACKET = 10; +INPUT_STATE_PACKET = 5; stateUnchange = true; unchangePacket = INPUT_STATE_PACKET; @@ -137,7 +137,6 @@ document.body.onkeyup = function(e){ }; - window.startSession = () => { let sd = remoteSessionDescription if (sd === '') { @@ -145,55 +144,65 @@ window.startSession = () => { } try { - pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(atob(sd))), - - // success - () => { - - setInterval(() => { - // prepare key - /* - const ( - ButtonA = iota - ButtonB - ButtonSelect - ButtonStart - ButtonUp - ButtonDown - ButtonLeft - ButtonRight - ) - */ - - if (stateUnchange || unchangePacket > 0) { - st = ""; - ["a", "b", "select", "start", "up", "down", "left", "right"].forEach(elem => { - st += keyState[elem]?1:0; - }); - ss = parseInt(st, 2); - console.log(`Key state string: ${st} ==> ${ss}`); - - // send - inputChannel.send(ss); - - stateUnchange = false; - unchangePacket--; - } - - }, 1000 / INPUT_FPS) - - - }, - - // error callback - (exp) => { - console.log("setRemoteDescription failed, we are doomed"); - console.log(exp); - }); + pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(atob(sd)))); } catch (e) { alert(e); } } +var timer = null; +function sendInput() { + // prepare key + /* + const ( + ButtonA = iota + ButtonB + ButtonSelect + ButtonStart + ButtonUp + ButtonDown + ButtonLeft + ButtonRight + ) + */ + + if (stateUnchange || unchangePacket > 0) { + st = ""; + ["a", "b", "select", "start", "up", "down", "left", "right"].forEach(elem => { + st += keyState[elem]?1:0; + }); + ss = parseInt(st, 2); + console.log(`Key state string: ${st} ==> ${ss}`); + + // send + inputChannel.send(ss); + + stateUnchange = false; + unchangePacket--; + } +} + +function startInput() { + if (timer == null) { + timer = setInterval(sendInput, 1000 / INPUT_FPS) + } +} + +function endInput() { + clearInterval(timer); + timer = null; +} + +pc.oniceconnectionstatechange = e => { + log(pc.iceConnectionState); + if (pc.iceConnectionState === "connected") { + startInput(); + } + else if (pc.iceConnectionState === "disconnected") { + endInput(); + } + +} + diff --git a/main.go b/main.go index 51972086..ed71c53c 100644 --- a/main.go +++ b/main.go @@ -14,33 +14,36 @@ import ( "github.com/gorilla/mux" ) -var webRTC *webrtc.WebRTC +// var webRTC *webrtc.WebRTC var width = 256 var height = 240 var gameName = "supermariobros.rom" +var FPS = 60 func init() { } -func startGame(path string, imageChannel chan *image.RGBA, inputChannel chan int) { - ui.Run([]string{path}, imageChannel, inputChannel) +func startGame(path string, imageChannel chan *image.RGBA, inputChannel chan int, webRTC *webrtc.WebRTC) { + ui.Run([]string{path}, imageChannel, inputChannel, webRTC) } func main() { - imageChannel := make(chan *image.RGBA, 100) + // imageChannel := make(chan *image.RGBA, 100) fmt.Println("http://localhost:8000") - webRTC = webrtc.NewWebRTC() + // webRTC = webrtc.NewWebRTC() router := mux.NewRouter() router.HandleFunc("/", getWeb).Methods("GET") router.HandleFunc("/session", postSession).Methods("POST") - go http.ListenAndServe(":8000", router) + // go http.ListenAndServe(":8000", router) + http.ListenAndServe(":8000", router) // start screenshot loop, wait for connection - go screenshotLoop(imageChannel) - startGame("games/"+gameName, imageChannel, webRTC.InputChannel) - time.Sleep(time.Minute) + + // go screenshotLoop(imageChannel) + // startGame("games/"+gameName, imageChannel, webRTC.InputChannel) + // time.Sleep(time.Minute) } func getWeb(w http.ResponseWriter, r *http.Request) { @@ -58,20 +61,34 @@ func postSession(w http.ResponseWriter, r *http.Request) { } r.Body.Close() + webRTC := webrtc.NewWebRTC() + localSession, err := webRTC.StartClient(string(bs), width, height) if err != nil { log.Fatalln(err) } + imageChannel := make(chan *image.RGBA, FPS) + go screenshotLoop(imageChannel, webRTC) + go startGame("games/" + gameName, imageChannel, webRTC.InputChannel, webRTC) + w.Write([]byte(localSession)) } -func screenshotLoop(imageChannel chan *image.RGBA) { +// func screenshotLoop(imageChannel chan *image.RGBA) { +func screenshotLoop(imageChannel chan *image.RGBA, webRTC *webrtc.WebRTC) { for image := range imageChannel { + // Client stopped + if webRTC.IsClosed() { + break + } + + // encode frame if webRTC.IsConnected() { yuv := util.RgbaToYuv(image) webRTC.ImageChannel <- yuv } - time.Sleep(10 * time.Millisecond) + // time.Sleep(10 * time.Millisecond) + time.Sleep(time.Duration(1000 / FPS) * time.Millisecond) } } diff --git a/ui/director.go b/ui/director.go index a6feedb6..6afcd6f3 100644 --- a/ui/director.go +++ b/ui/director.go @@ -6,6 +6,7 @@ import ( "time" "github.com/giongto35/game-online/nes" + "github.com/giongto35/game-online/webrtc" ) type View interface { @@ -20,14 +21,16 @@ type Director struct { timestamp float64 imageChannel chan *image.RGBA inputChannel chan int + webRTC *webrtc.WebRTC } -func NewDirector(imageChannel chan *image.RGBA, inputChannel chan int) *Director { +func NewDirector(imageChannel chan *image.RGBA, inputChannel chan int, webRTC *webrtc.WebRTC) *Director { // func NewDirector(audio *Audio, imageChannel chan *image.RGBA, inputChannel chan int) *Director { director := Director{} // director.audio = audio director.imageChannel = imageChannel director.inputChannel = inputChannel + director.webRTC = webRTC return &director } @@ -61,6 +64,11 @@ func (d *Director) Start(paths []string) { func (d *Director) Run() { for { + // quit game + if d.webRTC.IsClosed() { + break + } + d.Step() } d.SetView(nil) diff --git a/ui/run.go b/ui/run.go index 5822bd28..87bea59b 100644 --- a/ui/run.go +++ b/ui/run.go @@ -6,6 +6,7 @@ import ( "runtime" // "github.com/gordonklaus/portaudio" + "github.com/giongto35/game-online/webrtc" ) const ( @@ -23,7 +24,7 @@ func init() { runtime.LockOSThread() } -func Run(paths []string, imageChannel chan *image.RGBA, inputChannel chan int) { +func Run(paths []string, imageChannel chan *image.RGBA, inputChannel chan int, webRTC *webrtc.WebRTC) { // initialize audio // portaudio.Initialize() // defer portaudio.Terminate() @@ -35,7 +36,7 @@ func Run(paths []string, imageChannel chan *image.RGBA, inputChannel chan int) { // defer audio.Stop() // run director - director := NewDirector(imageChannel, inputChannel) + director := NewDirector(imageChannel, inputChannel, webRTC) // director := NewDirector(audio, imageChannel, inputChannel) director.Start(paths) } diff --git a/webrtc/webrtc.go b/webrtc/webrtc.go index 7c56445f..095109af 100644 --- a/webrtc/webrtc.go +++ b/webrtc/webrtc.go @@ -108,6 +108,7 @@ type WebRTC struct { connection *webrtc.PeerConnection encoder *vpxEncoder.VpxEncoder isConnected bool + isClosed bool // for yuvI420 image ImageChannel chan []byte InputChannel chan int @@ -164,6 +165,7 @@ func (w *WebRTC) StartClient(remoteSession string, width, height int) (string, e w.StopClient() } }) + //w.listenInputChannel() // Data channel w.connection.OnDataChannel(func(d *webrtc.DataChannel) { @@ -210,6 +212,7 @@ func (w *WebRTC) StopClient() { w.connection.Close() } w.connection = nil + w.isClosed = true } // IsConnected comment @@ -217,6 +220,10 @@ func (w *WebRTC) IsConnected() bool { return w.isConnected } +func (w *WebRTC) IsClosed() bool { + return w.isClosed +} + func (w *WebRTC) startStreaming(vp8Track *webrtc.Track) { fmt.Println("Start streaming") // send screenshot