diff --git a/Makefile b/Makefile index bcda505..d990620 100644 --- a/Makefile +++ b/Makefile @@ -6,10 +6,12 @@ ifeq ($(PREFIX),) # PREFIX is environment variable, but if it is not set, then s PREFIX := /usr/local endif -all: client server engine +all: client client2 server engine client: $(GAMEFILES) $(wildcard cmd/client/*.go) go build -ldflags "-X main.GitCommit=$(GIT_COMMIT)" ./cmd/client +client2: $(GAMEFILES) $(wildcard cmd/client2/*.go) + go build -ldflags "-X main.GitCommit=$(GIT_COMMIT)" ./cmd/client2 server: $(GAMEFILES) $(COORDFILEs) $(wildcard cmd/server/*.go) go build -ldflags "-X main.GitCommit=$(GIT_COMMIT)" ./cmd/server engine: $(GAMEFILES) diff --git a/cmd/client/main.go b/cmd/client/main.go index 65830e5..aaee2be 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -66,6 +66,20 @@ func receiveHandler(connection *websocket.Conn, output chan string) { output <- "Sentinal wins!" case coordinator.SessionRespBroadcastScoWin: output <- "Scourge wins!" + case coordinator.SessionRespBroadcastUpdate: + //we don't handle updates + case coordinator.SessionRespBroadcastScoJoin: + output <- "Scourge has joined the game" + case coordinator.SessionRespBroadcastSenJoin: + output <- "Sentinal has joined the game" + case coordinator.SessionRespBroadcastScoLeft: + output <- "Scourge has left the game" + case coordinator.SessionRespBroadcastSenLeft: + output <- "Sentinal has left the game" + case coordinator.SessionRespBroadcastSenReady: + output <- "Sentinal is ready" + case coordinator.SessionRespBroadcastScoReady: + output <- "scourge is ready" case coordinator.SessionRespBroadcastNone: case coordinator.SessionRespError: diff --git a/cmd/client2/backend.go b/cmd/client2/backend.go index e7faa76..64e9eae 100644 --- a/cmd/client2/backend.go +++ b/cmd/client2/backend.go @@ -10,7 +10,6 @@ import ( "time" "unicode/utf8" - "code.rocketnine.space/tslocum/cview" "git.saintnet.tech/stryan/snengame/internal/coordinator" "git.saintnet.tech/stryan/snengame/internal/game" "github.com/google/uuid" @@ -22,7 +21,7 @@ var interrupt chan os.Signal var pid int var matchID uuid.UUID -func receiveHandler(connection *websocket.Conn, output chan string) { +func receiveHandler(connection *websocket.Conn, container *UIContainer) { defer close(done) for { var resp coordinator.SessionCommandResult @@ -33,54 +32,67 @@ func receiveHandler(connection *websocket.Conn, output chan string) { switch resp.Result { case coordinator.SessionRespJoined1: pid = game.SentinalID - output <- "joined as sentinal" + container.Output <- "joined as sentinal" case coordinator.SessionRespJoined2: pid = game.ScourgeID - output <- "joined as scourge" + container.Output <- "joined as scourge" case coordinator.SessionRespFound: matchID = resp.MatchID - output <- "game found" + container.Output <- "game found" case coordinator.SessionRespPlayed: if resp.GameResult != nil { - output <- "played succesfully" switch resp.GameResult.ResultType { case game.ActCmd: - output <- resp.GameResult.ActionResult.String() + container.Output <- resp.GameResult.ActionResult.String() case game.StateCmd: - output <- resp.GameResult.StateResult.String() + container.State = resp.GameResult.StateResult + container.Updated <- true case game.DebugCmd: - output <- resp.GameResult.DebugResult.String() + container.Output <- resp.GameResult.DebugResult.String() } } else { - output <- "error playing" + container.Output <- "error playing" } case coordinator.SessionRespLeft: - output <- "game left" + container.Output <- "game left" break case coordinator.SessionRespBroadcastSenTurn: - output <- "Sentinal may take their turn" + container.Output <- "Sentinal may take their turn" case coordinator.SessionRespBroadcastScoTrun: - output <- "Scourge may take their turn" + container.Output <- "Scourge may take their turn" case coordinator.SessionRespBroadcastSenWin: - output <- "Sentinal wins!" + container.Output <- "Sentinal wins!" case coordinator.SessionRespBroadcastScoWin: - output <- "Scourge wins!" + container.Output <- "Scourge wins!" + case coordinator.SessionRespBroadcastUpdate: + container.GetUpdate <- true + case coordinator.SessionRespBroadcastScoJoin: + container.Output <- "Scourge has joined the game" + case coordinator.SessionRespBroadcastSenJoin: + container.Output <- "Sentinal has joined the game" + case coordinator.SessionRespBroadcastScoLeft: + container.Output <- "Scourge has left the game" + case coordinator.SessionRespBroadcastSenLeft: + container.Output <- "Sentinal has left the game" + case coordinator.SessionRespBroadcastSenReady: + container.Output <- "Sentinal is ready" + case coordinator.SessionRespBroadcastScoReady: + container.Output <- "scourge is ready" case coordinator.SessionRespBroadcastNone: case coordinator.SessionRespError: - output <- "generic error" + container.Output <- "generic error" default: - output <- "Received a server response we don't know how to handle" + container.Output <- "Received a server response we don't know how to handle" } } } -func backend(input chan string, outputview *cview.TextView) { +func backend(container *UIContainer) { done = make(chan interface{}) // Channel to indicate that the receiverHandler is done interrupt = make(chan os.Signal) // Channel to listen for interrupt signal to terminate gracefully cmd := make(chan coordinator.SessionCommand) - output := make(chan string) signal.Notify(interrupt, os.Interrupt) // Notify the interrupt channel for SIGINT hostname := flag.String("host", "localhost", "server hostname to connect to") @@ -89,21 +101,20 @@ func backend(input chan string, outputview *cview.TextView) { flag.Parse() port_s := strconv.Itoa(*port) socketUrl := "ws://" + *hostname + ":" + port_s + "/ws" - conn, _, err := websocket.DefaultDialer.Dial(socketUrl, nil) id := uuid.New() + conn, _, err := websocket.DefaultDialer.Dial(socketUrl, nil) if err != nil { log.Fatal("Error connecting to Websocket Server:", err) } defer conn.Close() - go receiveHandler(conn, output) - go GetCommand(id, cmd, input) - ticker := time.NewTicker(1 * time.Second) + go receiveHandler(conn, container) + go GetCommand(id, cmd, container) + ticker := time.NewTicker(2 * time.Second) // Our main loop for the client // We send our relevant packets here for { var c coordinator.SessionCommand - var o string select { case c = <-cmd: err := conn.WriteJSON(c) @@ -114,8 +125,25 @@ func backend(input chan string, outputview *cview.TextView) { if c.Command == coordinator.SessionCmdLeave { break } - case o = <-output: - outputview.SetText(o) + case <-container.GetUpdate: + if container.State != nil { + err = conn.WriteJSON(&coordinator.SessionCommand{ + ID: id, + MatchID: matchID, + Command: coordinator.SessionCmdPlay, + GameCommand: &game.Command{ + PlayerID: pid, + Type: game.StateCmd, + Cmd: "g", + }, + }) + if err != nil { + log.Println("Error during writing to websocker:", err) + return + } + container.Updated <- true + } + case <-interrupt: // We received a SIGINT (Ctrl + C). Terminate gracefully... log.Println("Received SIGINT interrupt signal. Closing all pending connections") @@ -153,11 +181,11 @@ func backend(input chan string, outputview *cview.TextView) { } } -func GetCommand(uid uuid.UUID, resp chan coordinator.SessionCommand, inputchan chan string) { +func GetCommand(uid uuid.UUID, resp chan coordinator.SessionCommand, container *UIContainer) { for { var cmd string var t int - input := <-inputchan + input := <-container.Input input_s := strings.Split(input, " ") t, err := strconv.Atoi(input_s[0]) if err != nil { diff --git a/cmd/client2/main.go b/cmd/client2/main.go index 74b9138..935f4fa 100644 --- a/cmd/client2/main.go +++ b/cmd/client2/main.go @@ -1,20 +1,32 @@ package main import ( + "fmt" + "code.rocketnine.space/tslocum/cview" + "git.saintnet.tech/stryan/snengame/internal/game" "github.com/gdamore/tcell/v2" ) +type UIContainer struct { + State *game.GameView + Updated chan bool + Output chan string + Input chan string + GetUpdate chan bool +} + func main() { app := cview.NewApplication() app.EnableMouse(true) - newPrimitive := func(text string) cview.Primitive { - tv := cview.NewTextView() - tv.SetTextAlign(cview.AlignCenter) - tv.SetText(text) - return tv + container := &UIContainer{ + State: nil, + Updated: make(chan bool), + Output: make(chan string), + Input: make(chan string), + GetUpdate: make(chan bool), } - inputchan := make(chan string) + grid := cview.NewGrid() grid.SetRows(3, 0, 0, 3, 3) grid.SetColumns(30, 0, 30) @@ -22,55 +34,95 @@ func main() { board := cview.NewTable() board.SetBorders(true) - cols, rows := 4, 2 - for r := 0; r < rows; r++ { - for c := 0; c < cols; c++ { - color := tcell.ColorWhite.TrueColor() - if c < 1 || r < 1 { - color = tcell.ColorYellow.TrueColor() - } - cell := cview.NewTableCell("test") - cell.SetTextColor(color) - cell.SetAlign(cview.AlignCenter) - board.SetCell(r, c, cell) - } - } - rows = 1 hand := cview.NewTable() hand.SetBorders(true) - for r := 0; r < rows; r++ { - for c := 0; c < cols; c++ { - color := tcell.ColorWhite.TrueColor() - if c < 1 || r < 1 { - color = tcell.ColorYellow.TrueColor() - } - cell := cview.NewTableCell("test") - cell.SetTextColor(color) - cell.SetAlign(cview.AlignCenter) - hand.SetCell(r, c, cell) - } - } - - playerside := newPrimitive("Player") - oppside := newPrimitive("Opponent") + playerside := cview.NewTextView() + playerside.SetText("Game has not started") + playerside.SetTextAlign(cview.AlignCenter) commandinput := cview.NewInputField() commandoutput := cview.NewTextView() commandinput.SetDoneFunc(func(key tcell.Key) { - inputchan <- commandinput.GetText() + container.Input <- commandinput.GetText() commandinput.SetText("") }) commandoutput.SetChangedFunc(func() { app.Draw() }) + playerside.SetChangedFunc(func() { + app.Draw() + }) + + go func() { + for { + select { + case <-container.Updated: + playerside.Clear() + var pv string + if container.State != nil && container.State.Player != nil { + ct := "Noone" + if container.State.CurrentTurn == game.SentinalID { + ct = "Sentinal" + } else if container.State.CurrentTurn == game.ScourgeID { + ct = "Scourge" + } + pv = fmt.Sprintf("Your Life: %v\nEnemy Life: %v\nEnemy Hand size: %v\nEnemy Deck Size: %v\n\nCT:%v CD: %v, HD %v, Status: %v\n", container.State.Player.Life, container.State.EnemyLife, container.State.EnemyHandSize, container.State.EnemyDeckSize, ct, container.State.CanDraw, container.State.HasDrawn, container.State.Status) + } else { + pv = fmt.Sprintf("%v", "Awaiting player info") + } + fmt.Fprintf(playerside, "%v", pv) + + for c := 0; c < 4; c++ { + color := tcell.ColorWhite.TrueColor() + be := "_" + if container.State != nil && container.State.Board != nil { + be = container.State.Board.Sentinal[c].String() + } + cell := cview.NewTableCell(be) + cell.SetTextColor(color) + cell.SetAlign(cview.AlignCenter) + board.SetCell(0, c, cell) + } + for c := 0; c < 4; c++ { + color := tcell.ColorWhite.TrueColor() + be := "_" + if container.State != nil && container.State.Board != nil { + be = container.State.Board.Scourge[c].String() + } + cell := cview.NewTableCell(be) + cell.SetTextColor(color) + cell.SetAlign(cview.AlignCenter) + board.SetCell(1, c, cell) + } + cols := 0 + if container.State != nil && container.State.Player != nil && container.State.Player.Hand != nil { + cols = len(container.State.Player.Hand) + } + for c := 0; c < cols; c++ { + color := tcell.ColorWhite.TrueColor() + ce := "_" + if container.State != nil && container.State.Player != nil && container.State.Player.Hand != nil { + ce = container.State.Player.Hand[c].String() + } + cell := cview.NewTableCell(ce) + cell.SetTextColor(color) + cell.SetAlign(cview.AlignCenter) + hand.SetCell(0, c, cell) + } + app.Draw() + case o := <-container.Output: + commandoutput.Clear() + fmt.Fprintf(commandoutput, "Result: %v", o) + } + } + }() grid.AddItem(playerside, 1, 0, 2, 1, 0, 100, false) grid.AddItem(board, 1, 1, 1, 1, 0, 100, false) grid.AddItem(hand, 2, 1, 1, 1, 0, 100, false) - grid.AddItem(oppside, 1, 2, 2, 1, 0, 100, false) grid.AddItem(commandinput, 3, 0, 1, 3, 0, 0, true) grid.AddItem(commandoutput, 4, 0, 1, 3, 0, 0, false) - go backend(inputchan, commandoutput) - + container.Updated <- true + go backend(container) app.SetRoot(grid, true) if err := app.Run(); err != nil { panic(err) diff --git a/go.mod b/go.mod index b721802..61f0c48 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module git.saintnet.tech/stryan/snengame go 1.16 require ( + code.rocketnine.space/tslocum/cview v1.5.6 // indirect + github.com/gdamore/tcell/v2 v2.4.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.4.2 ) diff --git a/go.sum b/go.sum index e8df9a1..665c5fb 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,38 @@ +code.rocketnine.space/tslocum/cbind v0.1.5 h1:i6NkeLLNPNMS4NWNi3302Ay3zSU6MrqOT+yJskiodxE= +code.rocketnine.space/tslocum/cbind v0.1.5/go.mod h1:LtfqJTzM7qhg88nAvNhx+VnTjZ0SXBJtxBObbfBWo/M= +code.rocketnine.space/tslocum/cview v1.5.6 h1:W0HJFIIgly3LzYoTitZIIaYDDqW0u/qmA0B6jWzw6R0= +code.rocketnine.space/tslocum/cview v1.5.6/go.mod h1:RogJMObbKuGiP8+9WsFsHpPeQQqgkCXgvTLxh7IH5eE= +github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell/v2 v2.2.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= +github.com/gdamore/tcell/v2 v2.3.11 h1:ECO6WqHGbKZ3HrSL7bG/zArMCmLaNr5vcjjMVnLHpzc= +github.com/gdamore/tcell/v2 v2.3.11/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= +github.com/gdamore/tcell/v2 v2.4.0 h1:W6dxJEmaxYvhICFoTY3WrLLEXsQ11SaFnKGVEXW57KM= +github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/internal/coordinator/coordinator.go b/internal/coordinator/coordinator.go index a1941ba..badb966 100644 --- a/internal/coordinator/coordinator.go +++ b/internal/coordinator/coordinator.go @@ -148,10 +148,29 @@ func (c *Coordinator) Coordinate(cmd *SessionCommand) *SessionCommandResult { func MatchWatcher(m *Session) { ticker := time.NewTicker(1 * time.Second) old_turn := -1 + var old_board *game.Board + old_sen_hand := -1 + old_sco_hand := -1 + old_sen_life := -1 + old_sco_life := -1 + sen_ready := false + sco_ready := false for { select { case <-ticker.C: if m.Active && m.Game != nil { + if m.Game.Status == game.StatusLobby && !sen_ready && m.Game.SentinalPlayer.Ready { + for _, v := range m.Broadcasts { + v <- SessionRespBroadcastSenReady + } + sen_ready = true + } + if m.Game.Status == game.StatusLobby && !sco_ready && m.Game.ScourgePlayer.Ready { + for _, v := range m.Broadcasts { + v <- SessionRespBroadcastScoReady + } + sco_ready = true + } if m.Game.Status == game.StatusSentinalWin { for _, v := range m.Broadcasts { v <- SessionRespBroadcastSenWin @@ -162,6 +181,33 @@ func MatchWatcher(m *Session) { v <- SessionRespBroadcastScoWin } } + if m.p1 == uuid.Nil && sen_ready { + for _, v := range m.Broadcasts { + v <- SessionRespBroadcastSenLeft + } + sen_ready = false + } + if m.p2 == uuid.Nil && sco_ready { + for _, v := range m.Broadcasts { + v <- SessionRespBroadcastScoLeft + } + sco_ready = false + } + if old_board == nil || old_board.Sentinal != m.Game.GameBoard.Sentinal || old_board.Scourge != m.Game.GameBoard.Scourge || old_sen_hand != len(m.Game.SentinalPlayer.Hand) || old_sco_hand != len(m.Game.ScourgePlayer.Hand) || old_sen_life != m.Game.SentinalPlayer.Life || old_sco_life != m.Game.ScourgePlayer.Life { + if old_board == nil { + old_board = m.Game.GameBoard + } else { + old_board.Sentinal = m.Game.GameBoard.Sentinal + old_board.Scourge = m.Game.GameBoard.Scourge + } + old_sen_hand = len(m.Game.SentinalPlayer.Hand) + old_sco_hand = len(m.Game.ScourgePlayer.Hand) + old_sen_life = m.Game.SentinalPlayer.Life + old_sco_life = m.Game.ScourgePlayer.Life + for _, v := range m.Broadcasts { + v <- SessionRespBroadcastUpdate + } + } if old_turn != m.Game.CurrentTurn { old_turn = m.Game.CurrentTurn if old_turn == game.SentinalID { diff --git a/internal/coordinator/session.go b/internal/coordinator/session.go index 0675bc8..9978f5f 100644 --- a/internal/coordinator/session.go +++ b/internal/coordinator/session.go @@ -19,18 +19,25 @@ const ( ) const ( - SessionRespFound SessionResp = "found" - SessionRespJoined1 = "joined p1" - SessionRespJoined2 = "joined p2" - SessionRespJoinError = "join error" - SessionRespLeft = "left" - SessionRespPlayed = "played" - SessionRespError = "generic error" - SessionRespBroadcastSenTurn = "Sentinal turn" - SessionRespBroadcastScoTrun = "Scourge turn" - SessionRespBroadcastSenWin = "Sentinal wins" - SessionRespBroadcastScoWin = "Scourge wins" - SessionRespBroadcastNone = "" + SessionRespFound SessionResp = "found" + SessionRespJoined1 = "joined p1" + SessionRespJoined2 = "joined p2" + SessionRespJoinError = "join error" + SessionRespLeft = "left" + SessionRespPlayed = "played" + SessionRespError = "generic error" + SessionRespBroadcastSenTurn = "Sentinal turn" + SessionRespBroadcastScoTrun = "Scourge turn" + SessionRespBroadcastSenWin = "Sentinal wins" + SessionRespBroadcastScoWin = "Scourge wins" + SessionRespBroadcastUpdate = "update" + SessionRespBroadcastSenJoin = "Sentinal joined" + SessionRespBroadcastScoJoin = "Scourge joined" + SessionRespBroadcastSenReady = "Sentinal player is ready" + SessionRespBroadcastScoReady = "Scourge player is ready" + SessionRespBroadcastSenLeft = "Sentinal player has left" + SessionRespBroadcastScoLeft = "Scourge player has left" + SessionRespBroadcastNone = "" ) type Session struct { @@ -61,11 +68,17 @@ func (s *Session) Join(id uuid.UUID) SessionResp { if s.p1 == uuid.Nil { s.p1 = id s.pMap[id] = game.SentinalID + for _, v := range s.Broadcasts { + v <- SessionRespBroadcastSenJoin + } s.Broadcasts[id] = make(chan SessionResp) return SessionRespJoined1 } else if s.p2 == uuid.Nil { s.p2 = id s.pMap[id] = game.ScourgeID + for _, v := range s.Broadcasts { + v <- SessionRespBroadcastScoJoin + } s.Broadcasts[id] = make(chan SessionResp) return SessionRespJoined2 } else { @@ -79,6 +92,7 @@ func (s *Session) Leave(id uuid.UUID) { } else if id == s.p2 { s.p2 = uuid.Nil } + delete(s.Broadcasts, id) if s.p1 == uuid.Nil && s.p2 == uuid.Nil { s.Game = nil } else if s.Game.Status != game.StatusDraw || s.Game.Status != game.StatusScourgeWin || s.Game.Status != game.StatusSentinalWin { diff --git a/internal/game/game.go b/internal/game/game.go index 10d64f5..4349164 100644 --- a/internal/game/game.go +++ b/internal/game/game.go @@ -158,18 +158,34 @@ func (g *Game) PlayerStateAct(id int, cmd string) *GameView { return NewView(id, g) case "b": //begin game - g.Status = StatusReady - //TODO check for ready on both accounts first - g.SentinalPlayer.Hand = g.SentinalDeck.Cards[len(g.SentinalDeck.Cards)-5 : len(g.SentinalDeck.Cards)] - g.SentinalDeck.Cards = g.SentinalDeck.Cards[0 : len(g.SentinalDeck.Cards)-5] + if g.SentinalPlayer.Ready && g.ScourgePlayer.Ready && g.Status == StatusLobby { + g.Status = StatusReady + if id == SentinalID { + g.SentinalPlayer.Hand = g.SentinalDeck.Cards[len(g.SentinalDeck.Cards)-5 : len(g.SentinalDeck.Cards)] + g.SentinalDeck.Cards = g.SentinalDeck.Cards[0 : len(g.SentinalDeck.Cards)-5] - g.ScourgePlayer.Hand = g.ScourgeDeck.Cards[len(g.ScourgeDeck.Cards)-5 : len(g.ScourgeDeck.Cards)] - g.ScourgeDeck.Cards = g.ScourgeDeck.Cards[0 : len(g.ScourgeDeck.Cards)-5] + g.ScourgePlayer.Hand = g.ScourgeDeck.Cards[len(g.ScourgeDeck.Cards)-6 : len(g.ScourgeDeck.Cards)] + g.ScourgeDeck.Cards = g.ScourgeDeck.Cards[0 : len(g.ScourgeDeck.Cards)-6] + g.CurrentTurn = SentinalID + } else { + g.SentinalPlayer.Hand = g.SentinalDeck.Cards[len(g.SentinalDeck.Cards)-6 : len(g.SentinalDeck.Cards)] + g.SentinalDeck.Cards = g.SentinalDeck.Cards[0 : len(g.SentinalDeck.Cards)-6] + + g.ScourgePlayer.Hand = g.ScourgeDeck.Cards[len(g.ScourgeDeck.Cards)-5 : len(g.ScourgeDeck.Cards)] + g.ScourgeDeck.Cards = g.ScourgeDeck.Cards[0 : len(g.ScourgeDeck.Cards)-5] + g.CurrentTurn = ScourgeID + } + } else { + if id == SentinalID { + g.SentinalPlayer.Ready = true + } else if id == ScourgeID { + g.ScourgePlayer.Ready = true + } + } case "s": //start turn if g.Status == StatusReady { //first turn g.Status = StatusPlaying - g.CurrentTurn = id } g.CanDraw = true g.HasDrawn = false diff --git a/internal/game/player.go b/internal/game/player.go index 5c227b4..745b70d 100644 --- a/internal/game/player.go +++ b/internal/game/player.go @@ -1,17 +1,19 @@ package game type Player struct { - Name string `json:"name"` - Id int `json:"id"` - Hand []*Card `json:"hand"` - Life int `json:"life"` + Name string `json:"name"` + Id int `json:"id"` + Hand []*Card `json:"hand"` + Life int `json:"life"` + Ready bool `json:"ready"` } func NewPlayer(name string, id int) *Player { return &Player{ - Name: name, - Id: id, - Hand: []*Card{}, - Life: 3, + Name: name, + Id: id, + Hand: []*Card{}, + Life: 3, + Ready: false, } }