mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-07-21 09:08:57 +00:00
Add closed channel
This commit is contained in:
parent
cb604b3503
commit
7a169ac2a4
4 changed files with 66 additions and 40 deletions
34
main.go
34
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue