Add 2 players

This commit is contained in:
giongto35 2019-04-08 17:28:11 +08:00
parent ee2dfa726e
commit 387cfc91b0
5 changed files with 74 additions and 49 deletions

28
main.go
View file

@ -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
}
}

View file

@ -23,6 +23,10 @@ textarea {
</select>
<button id="play" onclick="window.startGame()">Play</button>
Room ID: <input type="text" id="roomID"><br>
Play as player(1,2): <select id="playerIndex">
<option value="1">1</option>
<option value="2">2</option>
</select>
<br/><br/>
@ -31,14 +35,15 @@ Room ID: <input type="text" id="roomID"><br>
<h3>Instruction</h3>
<div>
C is start button. Use it to start game<br />
V is select button <br />
Player 1
Use Up, Down, Left, Right to Move <br />
Z to jump (A) <br />
X to sprint (B) <br />
C is start button <br />
V is select button <br />
S to save <br />
L to load <br />
<!--Fullscreen media for better gaming experience<br /-->
</div><br>
@ -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);

View file

@ -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

View file

@ -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)
}

View file

@ -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
})
})