From 7a169ac2a463c19c7e15dbdd4d06eed432de8aad Mon Sep 17 00:00:00 2001 From: giongto35 Date: Wed, 10 Apr 2019 01:25:11 +0800 Subject: [PATCH] Add closed channel --- main.go | 34 ++++++++++++++++++++++++---------- ui/director.go | 27 +++++++++++++++++++-------- ui/gameview.go | 38 ++++++++++++++++++++------------------ ui/run.go | 7 +++---- 4 files changed, 66 insertions(+), 40 deletions(-) diff --git a/main.go b/main.go index d476b5b2..04782ce5 100644 --- a/main.go +++ b/main.go @@ -44,10 +44,15 @@ type WSPacket struct { PlayerIndex int `json:"player_index"` } +// 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 inputChannel chan int - rtcSessions []*webrtc.WebRTC + // closedChannel is to fire exit event when there is no webRTC session running + closedChannel chan bool + + rtcSessions []*webrtc.WebRTC } var rooms map[string]*Room @@ -66,8 +71,8 @@ func main() { http.ListenAndServe(":8000", nil) } -func startGame(path string, roomID string, imageChannel chan *image.RGBA, inputChannel chan int) { - ui.Run([]string{path}, roomID, imageChannel, inputChannel) +func startGame(path string, roomID string, imageChannel chan *image.RGBA, inputChannel chan int, closedChannel chan bool) { + ui.Run([]string{path}, roomID, imageChannel, inputChannel, closedChannel) } func getWeb(w http.ResponseWriter, r *http.Request) { @@ -78,18 +83,20 @@ func getWeb(w http.ResponseWriter, r *http.Request) { w.Write(bs) } -// init initilize room returns roomID +// init initilizes a room returns roomID func initRoom(roomID, gameName string) string { roomID = generateRoomID() imageChannel := make(chan *image.RGBA, 100) inputChannel := make(chan int, 100) + closedChannel := make(chan bool) rooms[roomID] = &Room{ - imageChannel: imageChannel, - inputChannel: inputChannel, - rtcSessions: []*webrtc.WebRTC{}, + imageChannel: imageChannel, + inputChannel: inputChannel, + closedChannel: closedChannel, + rtcSessions: []*webrtc.WebRTC{}, } go fanoutScreen(imageChannel, roomID) - go startGame("games/"+gameName, roomID, imageChannel, inputChannel) + go startGame("games/"+gameName, roomID, imageChannel, inputChannel, closedChannel) return roomID } @@ -208,6 +215,8 @@ func generateRoomID() string { // fanoutScreen fanout outputs to all webrtc in the same room func fanoutScreen(imageChannel chan *image.RGBA, roomID string) { for image := range imageChannel { + isRoomRunning := false + yuv := util.RgbaToYuv(image) for _, webRTC := range rooms[roomID].rtcSessions { // Client stopped @@ -220,6 +229,11 @@ func fanoutScreen(imageChannel chan *image.RGBA, roomID string) { if webRTC.IsConnected() { webRTC.ImageChannel <- yuv } + isRoomRunning = true + } + + if isRoomRunning == false { + rooms[roomID].closedChannel <- true } } } @@ -238,8 +252,8 @@ func faninInput(inputChannel chan int, webRTC *webrtc.WebRTC, playerIndex int) { input := <-webRTC.InputChannel // the first 10 bits belong to player 1 // the next 10 belongs to player 2 ... - // We standardize and put it to inputChannel (20 bytes) - input = input << ((uint(playerIndex) - 1) * ui.NumKeys / 2) + // We standardize and put it to inputChannel (20 bits) + input = input << ((uint(playerIndex) - 1) * ui.NumKeys) inputChannel <- input } } diff --git a/ui/director.go b/ui/director.go index 1408f974..ee6194e9 100644 --- a/ui/director.go +++ b/ui/director.go @@ -16,19 +16,20 @@ type View interface { type Director struct { // audio *Audio - view View - timestamp float64 - imageChannel chan *image.RGBA - inputChannel chan int - roomID string + view View + timestamp float64 + imageChannel chan *image.RGBA + inputChannel chan int + closedChannel chan bool + roomID string } -func NewDirector(roomID string, imageChannel chan *image.RGBA, inputChannel chan int) *Director { - // func NewDirector(audio *Audio, imageChannel chan *image.RGBA, inputChannel chan int) *Director { +func NewDirector(roomID string, imageChannel chan *image.RGBA, inputChannel chan int, closedChannel chan bool) *Director { director := Director{} // director.audio = audio director.imageChannel = imageChannel director.inputChannel = inputChannel + director.closedChannel = closedChannel director.roomID = roomID return &director } @@ -61,9 +62,17 @@ func (d *Director) Start(paths []string) { } func (d *Director) Run() { +L: for { // quit game - // TODO: Check if noone using + + select { + // if there is event from close channel => the game is ended + case <-d.closedChannel: + log.Println("Closed game") + break L + default: + } d.Step() } @@ -71,6 +80,7 @@ func (d *Director) Run() { } func (d *Director) PlayGame(path string) { + // Generate hash that is indentifier of a room (game path + ropomID) hash, err := hashFile(path, d.roomID) if err != nil { log.Fatalln(err) @@ -79,5 +89,6 @@ func (d *Director) PlayGame(path string) { if err != nil { log.Fatalln(err) } + // Set GameView as current view d.SetView(NewGameView(d, console, path, hash, d.imageChannel, d.inputChannel)) } diff --git a/ui/gameview.go b/ui/gameview.go index 3a828a9e..5eb44633 100644 --- a/ui/gameview.go +++ b/ui/gameview.go @@ -34,49 +34,57 @@ const ( save2 load2 ) -const NumKeys = 20 +const NumKeys = 10 type GameView struct { director *Director console *nes.Console title string hash string - record bool - frames []image.Image // equivalent to the list key pressed const above - keyPressed [20]bool + keyPressed [NumKeys * 2]bool imageChannel chan *image.RGBA inputChannel chan int } func NewGameView(director *Director, console *nes.Console, title, hash string, imageChannel chan *image.RGBA, inputChannel chan int) View { - gameview := &GameView{director, console, title, hash, false, nil, [NumKeys]bool{false}, imageChannel, inputChannel} + gameview := &GameView{ + director: director, + console: console, + title: title, + hash: hash, + keyPressed: [NumKeys * 2]bool{false}, + imageChannel: imageChannel, + inputChannel: inputChannel, + } + go gameview.ListenToInputChannel() return gameview } +// ListenToInputChannel listen from input channel streamm, which is exposed to WebRTC session func (view *GameView) ListenToInputChannel() { for { keysInBinary := <-view.inputChannel - for i := 0; i < NumKeys; i++ { + for i := 0; i < NumKeys*2; i++ { view.keyPressed[i] = ((keysInBinary & 1) == 1) keysInBinary = keysInBinary >> 1 } } } +// Enter enter the game view. func (view *GameView) Enter() { // Always reset game + // Legacy Audio code. TODO: Add it back to support audio // view.console.SetAudioChannel(view.director.audio.channel) // view.console.SetAudioSampleRate(view.director.audio.sampleRate) + // load state - //if err := view.console.LoadState(savePath(view.hash)); err == nil { - //return - //} else { view.console.Reset() - //} + // load sram cartridge := view.console.Cartridge if cartridge.Battery != 0 { @@ -86,6 +94,7 @@ func (view *GameView) Enter() { } } +// Exit ... func (view *GameView) Exit() { // view.console.SetAudioChannel(nil) // view.console.SetAudioSampleRate(0) @@ -94,29 +103,22 @@ func (view *GameView) Exit() { if cartridge.Battery != 0 { writeSRAM(sramPath(view.hash), cartridge.SRAM) } - // save state - //view.console.SaveState(savePath(view.hash)) } +// Update is called for every period of time, dt is the elapsed time from the last frame func (view *GameView) Update(t, dt float64) { if dt > 1 { dt = 0 } console := view.console - //updateControllers(window, console) view.updateControllers() console.StepSeconds(dt) // fps to set frame view.imageChannel <- console.Buffer() - - if view.record { - view.frames = append(view.frames, copyImage(console.Buffer())) - } } func (view *GameView) updateControllers() { - // TODO: switch case // Divide keyPressed to player 1 and player 2 // First 10 keys are player 1 var player1Keys [8]bool diff --git a/ui/run.go b/ui/run.go index 642fc457..90f03829 100644 --- a/ui/run.go +++ b/ui/run.go @@ -20,8 +20,8 @@ func init() { runtime.LockOSThread() } -func Run(paths []string, roomID string, imageChannel chan *image.RGBA, inputChannel chan int) { - // TODO: stream audio +func Run(paths []string, roomID string, imageChannel chan *image.RGBA, inputChannel chan int, closedChannel chan bool) { + // TODO: Add these code back when we support stream audio // initialize audio // portaudio.Initialize() // defer portaudio.Terminate() @@ -33,7 +33,6 @@ func Run(paths []string, roomID string, imageChannel chan *image.RGBA, inputChan // defer audio.Stop() // run director - director := NewDirector(roomID, imageChannel, inputChannel) - // director := NewDirector(audio, imageChannel, inputChannel) + director := NewDirector(roomID, imageChannel, inputChannel, closedChannel) director.Start(paths) }