diff --git a/main.go b/main.go index 52b1cb5c..a0142d9f 100644 --- a/main.go +++ b/main.go @@ -41,9 +41,10 @@ type IndexPageData struct { var upgrader = websocket.Upgrader{} type WSPacket struct { - ID string `json:"id"` - Data string `json:"data"` - RoomID string `json:"room_id"` + ID string `json:"id"` + Data string `json:"data"` + RoomID string `json:"room_id"` + PlayerIndex int `json:"player_index"` } type SessionPacket struct { @@ -123,13 +124,14 @@ func initRoom(roomID, gameName string) string { return roomID } -func startSession(webRTC *webrtc.WebRTC, gameName string, roomID string) string { +func startSession(webRTC *webrtc.WebRTC, gameName string, roomID string, playerIndex int) string { if roomID == "" { roomID = initRoom(roomID, gameName) } + // TODO: Might have race condition rooms[roomID].rtcSessions = append(rooms[roomID].rtcSessions, webRTC) - faninInput(rooms[roomID].inputChannel, webRTC) + faninInput(rooms[roomID].inputChannel, webRTC, playerIndex) return roomID } @@ -152,6 +154,7 @@ func ws(w http.ResponseWriter, r *http.Request) { var gameName string var roomID string + var playerIndex int for !isDone { mt, message, err := c.ReadMessage() @@ -174,6 +177,7 @@ func ws(w http.ResponseWriter, r *http.Request) { case "ping": gameName = req.Data roomID = req.RoomID + playerIndex = req.PlayerIndex log.Println("Ping from server with game:", gameName) res.ID = "pong" @@ -200,7 +204,7 @@ func ws(w http.ResponseWriter, r *http.Request) { case "start": log.Println("Starting game") res.ID = "start" - res.RoomID = startSession(webRTC, gameName, roomID) + res.RoomID = startSession(webRTC, gameName, roomID, playerIndex) isDone = true } @@ -262,7 +266,7 @@ func postSession(w http.ResponseWriter, r *http.Request) { // if there is room, reuse image channel, add webRTC session } rooms[roomID].rtcSessions = append(rooms[roomID].rtcSessions, webRTC) - faninInput(rooms[roomID].inputChannel, webRTC) + faninInput(rooms[roomID].inputChannel, webRTC, 1) res := SessionPacket{ SDP: localSession, @@ -283,7 +287,7 @@ func generateRoomID() string { return roomID } -// func fanoutScreen(imageChannel chan *image.RGBA) { +// fanoutScreen fanout outputs to all webrtc in the same room func fanoutScreen(imageChannel chan *image.RGBA, roomID string) { for image := range imageChannel { yuv := util.RgbaToYuv(image) @@ -302,8 +306,8 @@ func fanoutScreen(imageChannel chan *image.RGBA, roomID string) { } } -// func faninInput(input chan int) { -func faninInput(inputChannel chan int, webRTC *webrtc.WebRTC) { +// faninInput fan-in of the same room to inputChannel +func faninInput(inputChannel chan int, webRTC *webrtc.WebRTC, playerIndex int) { go func() { for { // Client stopped @@ -314,6 +318,10 @@ func faninInput(inputChannel chan int, webRTC *webrtc.WebRTC) { // encode frame if webRTC.IsConnected() { 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) inputChannel <- input } } diff --git a/static/index_ws.html b/static/index_ws.html index af57ae65..acb75d43 100644 --- a/static/index_ws.html +++ b/static/index_ws.html @@ -23,6 +23,10 @@ textarea { Room ID:
+Play as player(1,2):

@@ -31,14 +35,15 @@ Room ID:

Instruction

+ C is start button. Use it to start game
+ V is select button
+ + Player 1 Use Up, Down, Left, Right to Move
Z to jump (A)
X to sprint (B)
- C is start button
- V is select button
S to save
L to load
-

@@ -77,8 +82,7 @@ window.startGame = () => { conn.onopen = () => { log("WebSocket is opened. Send ping"); - roomID = document.getElementById('roomID').value - conn.send(JSON.stringify({"id": "ping", "data": gameOp.value, "room_id": roomID})); + conn.send(JSON.stringify({"id": "ping", "data": gameOp.value, "room_id": roomID.value, "player_index": parseInt(playerIndex.value, 10)})); } conn.onerror = error => { @@ -103,7 +107,7 @@ window.startGame = () => { break; case "start": log("Got start"); - document.getElementById('roomID').value = d["room_id"] + roomID.value = d["room_id"] break; } } @@ -247,7 +251,7 @@ function sendInput() { if (stateUnchange || unchangePacket > 0) { st = ""; - ["a", "b", "select", "start", "up", "down", "left", "right", "save", "load"].forEach(elem => { + ["a", "b", "select", "start", "up", "down", "left", "right", "save", "load"].reverse().forEach(elem => { st += keyState[elem]?1:0; }); ss = parseInt(st, 2); diff --git a/ui/director.go b/ui/director.go index 197dc0b0..1408f974 100644 --- a/ui/director.go +++ b/ui/director.go @@ -45,7 +45,6 @@ func (d *Director) SetView(view View) { } func (d *Director) Step() { - //gl.Clear(gl.COLOR_BUFFER_BIT) timestamp := float64(time.Now().Nanosecond()) / float64(time.Second) dt := timestamp - d.timestamp d.timestamp = timestamp diff --git a/ui/gameview.go b/ui/gameview.go index 75d86867..3a828a9e 100644 --- a/ui/gameview.go +++ b/ui/gameview.go @@ -4,7 +4,6 @@ package ui import ( "image" - "fmt" // "strconv" "github.com/giongto35/cloud-game/nes" @@ -12,6 +11,31 @@ import ( const padding = 0 +// List key pressed +const ( + a1 = iota + b1 + select1 + start1 + up1 + down1 + left1 + right1 + save1 + load1 + a2 + b2 + select2 + start2 + up2 + down2 + left2 + right2 + save2 + load2 +) +const NumKeys = 20 + type GameView struct { director *Director console *nes.Console @@ -20,29 +44,25 @@ type GameView struct { record bool frames []image.Image - keyPressed [10]bool + // equivalent to the list key pressed const above + keyPressed [20]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, [10]bool{false}, imageChannel, inputChannel} + gameview := &GameView{director, console, title, hash, false, nil, [NumKeys]bool{false}, imageChannel, inputChannel} go gameview.ListenToInputChannel() return gameview } func (view *GameView) ListenToInputChannel() { for { - key := <-view.inputChannel - s := fmt.Sprintf("%.10b", key) - fmt.Println(s) - for i := 0; i < len(s); i++ { - if s[i] == '1' { - view.keyPressed[i] = true - } else { - view.keyPressed[i] = false - } + keysInBinary := <-view.inputChannel + for i := 0; i < NumKeys; i++ { + view.keyPressed[i] = ((keysInBinary & 1) == 1) + keysInBinary = keysInBinary >> 1 } } } @@ -85,7 +105,6 @@ func (view *GameView) Update(t, dt float64) { console := view.console //updateControllers(window, console) view.updateControllers() - //fmt.Println(console.Buffer()) console.StepSeconds(dt) // fps to set frame @@ -98,27 +117,20 @@ func (view *GameView) Update(t, dt float64) { func (view *GameView) updateControllers() { // TODO: switch case - // var buttons [8]bool - // buttons[nes.ButtonLeft] = view.keyPressed[37] - // buttons[nes.ButtonUp] = view.keyPressed[38] - // buttons[nes.ButtonRight] = view.keyPressed[39] - // buttons[nes.ButtonDown] = view.keyPressed[40] - // buttons[nes.ButtonA] = view.keyPressed[32] - // buttons[nes.ButtonB] = view.keyPressed[17] - // buttons[nes.ButtonStart] = view.keyPressed[13] - // buttons[nes.ButtonSelect] = view.keyPressed[16] - // view.console.Controller1.SetButtons(buttons) - var gameKeys [8]bool - copy(gameKeys[:], view.keyPressed[:8]) + // Divide keyPressed to player 1 and player 2 + // First 10 keys are player 1 + var player1Keys [8]bool + copy(player1Keys[:], view.keyPressed[:8]) + var player2Keys [8]bool + copy(player2Keys[:], view.keyPressed[10:18]) - if view.keyPressed[8] { - fmt.Println("saving", view.hash) + view.console.Controller1.SetButtons(player1Keys) + view.console.Controller2.SetButtons(player2Keys) + + if view.keyPressed[save1] || view.keyPressed[save2] { view.console.SaveState(savePath(view.hash)) } - if view.keyPressed[9] { - fmt.Println("loading", view.hash) + if view.keyPressed[load1] || view.keyPressed[load2] { view.console.LoadState(savePath(view.hash)) } - - view.console.Controller1.SetButtons(gameKeys) } diff --git a/webrtc/webrtc.go b/webrtc/webrtc.go index bccf1da0..44ecefd7 100644 --- a/webrtc/webrtc.go +++ b/webrtc/webrtc.go @@ -181,6 +181,8 @@ func (w *WebRTC) StartClient(remoteSession string, width, height int) (string, e d.OnMessage(func(msg webrtc.DataChannelMessage) { //fmt.Printf("Message from DataChannel '%s': '%s' byte '%b'\n", d.Label(), string(msg.Data), msg.Data) i, _ := strconv.Atoi(string(msg.Data)) + // TODO: check if we can send int directly + // Input is key state, represented as binary string, 1001011. We compress it to binary number w.InputChannel <- i }) })