intro server and client
This commit is contained in:
parent
beab95579c
commit
277189f229
15
Makefile
15
Makefile
@ -1,4 +1,5 @@
|
|||||||
GOFILES=$(wildcard *.go)
|
GAMEFILES=$(wildcard internal/game/*.go)
|
||||||
|
COORDFILEs=$(wildcard internal/coordinator/*.go)
|
||||||
PROG=snengame
|
PROG=snengame
|
||||||
#set variables
|
#set variables
|
||||||
GIT_COMMIT := $(shell git rev-list -1 HEAD)
|
GIT_COMMIT := $(shell git rev-list -1 HEAD)
|
||||||
@ -8,11 +9,17 @@ endif
|
|||||||
|
|
||||||
$(PROG): $(GOFILES)
|
$(PROG): $(GOFILES)
|
||||||
go build -ldflags "-X main.GitCommit=$(GIT_COMMIT)"
|
go build -ldflags "-X main.GitCommit=$(GIT_COMMIT)"
|
||||||
|
client: $(GAMEFILES) $(wildcard cmd/client/*.go)
|
||||||
|
go build -ldflags "-X main.GitCommit=$(GIT_COMMIT)" ./cmd/client
|
||||||
|
server: $(GAMEFILES) $(wildcard cmd/server/*.go)
|
||||||
|
go build -ldflags "-X main.GitCommit=$(GIT_COMMIT)" ./cmd/server
|
||||||
|
engine: $(GAMEFILES)
|
||||||
|
go build -ldflags "-X main.GitCommit=$(GIT_COMMIT)" ./cmd/engine
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f snengame
|
rm -f client server engine
|
||||||
run: snengame
|
run: engine
|
||||||
./snengame
|
./engine
|
||||||
install: $(SNENGAME)
|
install: $(SNENGAME)
|
||||||
install -d $(DESTDIR)$(PREFIX)/bin
|
install -d $(DESTDIR)$(PREFIX)/bin
|
||||||
install -m 755 $(PROG) $(DESTDIR)$(PREFIX)/bin
|
install -m 755 $(PROG) $(DESTDIR)$(PREFIX)/bin
|
||||||
|
129
cmd/client/main.go
Normal file
129
cmd/client/main.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.saintnet.tech/stryan/snengame/internal/coordinator"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
var done chan interface{}
|
||||||
|
var interrupt chan os.Signal
|
||||||
|
|
||||||
|
func receiveHandler(connection *websocket.Conn) {
|
||||||
|
defer close(done)
|
||||||
|
for {
|
||||||
|
var resp coordinator.SessionCommandResult
|
||||||
|
err := connection.ReadJSON(&resp)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error in receive:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("Received: %s\n", resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
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)
|
||||||
|
|
||||||
|
signal.Notify(interrupt, os.Interrupt) // Notify the interrupt channel for SIGINT
|
||||||
|
|
||||||
|
socketUrl := "ws://localhost:7636" + "/ws"
|
||||||
|
conn, _, err := websocket.DefaultDialer.Dial(socketUrl, nil)
|
||||||
|
id := uuid.New()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error connecting to Websocket Server:", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
go receiveHandler(conn)
|
||||||
|
go GetCommand(id, cmd)
|
||||||
|
|
||||||
|
// Our main loop for the client
|
||||||
|
// We send our relevant packets here
|
||||||
|
for {
|
||||||
|
var c coordinator.SessionCommand
|
||||||
|
select {
|
||||||
|
case c = <-cmd:
|
||||||
|
// Send an echo packet every second
|
||||||
|
err := conn.WriteJSON(c)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error during writing to websocket:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-interrupt:
|
||||||
|
// We received a SIGINT (Ctrl + C). Terminate gracefully...
|
||||||
|
log.Println("Received SIGINT interrupt signal. Closing all pending connections")
|
||||||
|
|
||||||
|
// Close our websocket connection
|
||||||
|
err := conn.WriteJSON(coordinator.SessionCommand{
|
||||||
|
ID: id,
|
||||||
|
Command: coordinator.SessionCmdLeave,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error during closing websocket:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
log.Println("Receiver Channel Closed! Exiting....")
|
||||||
|
case <-time.After(time.Duration(1) * time.Second):
|
||||||
|
log.Println("Timeout in closing receiving channel. Exiting....")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCommand(uid uuid.UUID, resp chan coordinator.SessionCommand) {
|
||||||
|
for {
|
||||||
|
var cmd string
|
||||||
|
var t int
|
||||||
|
fmt.Print("> ")
|
||||||
|
_, err := fmt.Scanf("%d", &t)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
if t == -1 {
|
||||||
|
panic("quitting")
|
||||||
|
}
|
||||||
|
_, err = fmt.Scanf("%s", &cmd)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
cmd = strings.TrimSpace(cmd)
|
||||||
|
switch t {
|
||||||
|
case 0:
|
||||||
|
//session
|
||||||
|
switch coordinator.SessionCmd(cmd) {
|
||||||
|
case coordinator.SessionCmdQuery:
|
||||||
|
resp <- coordinator.SessionCommand{
|
||||||
|
ID: uid,
|
||||||
|
Command: coordinator.SessionCmdQuery,
|
||||||
|
}
|
||||||
|
case coordinator.SessionCmdJoin:
|
||||||
|
resp <- coordinator.SessionCommand{
|
||||||
|
ID: uid,
|
||||||
|
Command: coordinator.SessionCmdJoin,
|
||||||
|
}
|
||||||
|
case coordinator.SessionCmdLeave:
|
||||||
|
resp <- coordinator.SessionCommand{
|
||||||
|
ID: uid,
|
||||||
|
Command: coordinator.SessionCmdLeave,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
cmd/server/main.go
Normal file
9
cmd/server/main.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "git.saintnet.tech/stryan/snengame/internal/coordinator"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
c := coordinator.NewCoordinator()
|
||||||
|
c.Start()
|
||||||
|
coordinator.Serve(c)
|
||||||
|
}
|
7
go.mod
7
go.mod
@ -2,4 +2,9 @@ module git.saintnet.tech/stryan/snengame
|
|||||||
|
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require github.com/gorilla/websocket v1.4.2
|
require (
|
||||||
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.4.2
|
||||||
|
)
|
||||||
|
|
||||||
|
replace git.saintnet.tech/stryan/snengame/internal => ./internal
|
||||||
|
2
go.sum
2
go.sum
@ -1,2 +1,4 @@
|
|||||||
|
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 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
83
internal/coordinator/coordinator.go
Normal file
83
internal/coordinator/coordinator.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package coordinator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.saintnet.tech/stryan/snengame/internal/game"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Coordinator struct {
|
||||||
|
Match *Session
|
||||||
|
PlayerQueueChan chan uuid.UUID
|
||||||
|
CallbackChan map[uuid.UUID]chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCoordinator() *Coordinator {
|
||||||
|
return &Coordinator{
|
||||||
|
Match: NewSession(),
|
||||||
|
PlayerQueueChan: make(chan uuid.UUID),
|
||||||
|
CallbackChan: make(map[uuid.UUID]chan bool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Coordinator) Start() {
|
||||||
|
go func() {
|
||||||
|
m := NewSession()
|
||||||
|
var p1, p2 uuid.UUID
|
||||||
|
p1 = <-c.PlayerQueueChan
|
||||||
|
fmt.Println("p1 join")
|
||||||
|
p2 = <-c.PlayerQueueChan
|
||||||
|
fmt.Println("p2 join")
|
||||||
|
c.Match = m
|
||||||
|
m.Game = game.NewGame()
|
||||||
|
c.CallbackChan[p1] <- true
|
||||||
|
c.CallbackChan[p2] <- true
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
time.Sleep(5)
|
||||||
|
if c.Match.Game == nil {
|
||||||
|
log.Println("clearing old match")
|
||||||
|
c.Match = nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Coordinator) Coordinate(cmd *SessionCommand) *SessionCommandResult {
|
||||||
|
switch cmd.Command {
|
||||||
|
case SessionCmdQuery:
|
||||||
|
c.CallbackChan[cmd.ID] = make(chan bool)
|
||||||
|
c.PlayerQueueChan <- cmd.ID
|
||||||
|
<-c.CallbackChan[cmd.ID]
|
||||||
|
return &SessionCommandResult{
|
||||||
|
ID: cmd.ID,
|
||||||
|
Result: SessionRespFound,
|
||||||
|
}
|
||||||
|
case SessionCmdJoin:
|
||||||
|
resp := c.Match.Join(cmd.ID)
|
||||||
|
return &SessionCommandResult{
|
||||||
|
ID: cmd.ID,
|
||||||
|
Result: resp,
|
||||||
|
}
|
||||||
|
case SessionCmdLeave:
|
||||||
|
c.Match.Leave(cmd.ID)
|
||||||
|
return &SessionCommandResult{
|
||||||
|
ID: cmd.ID,
|
||||||
|
Result: SessionRespLeft,
|
||||||
|
}
|
||||||
|
case SessionCmdPlay:
|
||||||
|
resp := c.Match.Play(cmd.ID, cmd.GameCommand)
|
||||||
|
return &SessionCommandResult{
|
||||||
|
ID: cmd.ID,
|
||||||
|
Result: SessionRespPlayed,
|
||||||
|
GameResult: resp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &SessionCommandResult{
|
||||||
|
ID: cmd.ID,
|
||||||
|
Result: SessionRespError,
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package engine
|
package coordinator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
@ -9,9 +9,9 @@ import (
|
|||||||
|
|
||||||
var upgrader = websocket.Upgrader{} // use default options
|
var upgrader = websocket.Upgrader{} // use default options
|
||||||
|
|
||||||
func serve() {
|
func Serve(c *Coordinator) {
|
||||||
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
||||||
serveWs(w, r)
|
serveWs(c, w, r)
|
||||||
})
|
})
|
||||||
err := http.ListenAndServe(":7636", nil)
|
err := http.ListenAndServe(":7636", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -19,7 +19,7 @@ func serve() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveWs(w http.ResponseWriter, r *http.Request) {
|
func serveWs(c *Coordinator, w http.ResponseWriter, r *http.Request) {
|
||||||
// Upgrade our raw HTTP connection to a websocket based one
|
// Upgrade our raw HTTP connection to a websocket based one
|
||||||
conn, err := upgrader.Upgrade(w, r, nil)
|
conn, err := upgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -30,18 +30,21 @@ func serveWs(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// The event loop
|
// The event loop
|
||||||
for {
|
for {
|
||||||
var cmd Command
|
var cmd SessionCommand
|
||||||
var resp CommandResult
|
|
||||||
err := conn.ReadJSON(&cmd)
|
err := conn.ReadJSON(&cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error during message reading:", err)
|
log.Println("Error during message reading:", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
log.Printf("Received: %s", cmd)
|
log.Printf("Received: %s", cmd)
|
||||||
|
resp := c.Coordinate(&cmd)
|
||||||
err = conn.WriteJSON(resp)
|
err = conn.WriteJSON(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error during message writing:", err)
|
log.Println("Error during message writing:", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if resp.Result == SessionRespLeft {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
98
internal/coordinator/session.go
Normal file
98
internal/coordinator/session.go
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package coordinator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.saintnet.tech/stryan/snengame/internal/game"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SessionCmd string
|
||||||
|
type SessionResp string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SessionCmdQuery SessionCmd = "query"
|
||||||
|
SessionCmdJoin = "join"
|
||||||
|
SessionCmdLeave = "leave"
|
||||||
|
SessionCmdPlay = "play"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SessionRespFound SessionResp = "found"
|
||||||
|
SessionRespJoined1 = "joined p1"
|
||||||
|
SessionRespJoined2 = "joined p2"
|
||||||
|
SessionRespJoinError = "join error"
|
||||||
|
SessionRespLeft = "left"
|
||||||
|
SessionRespPlayed = "played"
|
||||||
|
SessionRespError = "generic error"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
p1 uuid.UUID
|
||||||
|
p2 uuid.UUID
|
||||||
|
pMap map[uuid.UUID]int
|
||||||
|
Game *game.Game
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSession() *Session {
|
||||||
|
return &Session{
|
||||||
|
p1: uuid.Nil,
|
||||||
|
p2: uuid.Nil,
|
||||||
|
pMap: make(map[uuid.UUID]int),
|
||||||
|
Game: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) Join(id uuid.UUID) SessionResp {
|
||||||
|
if s.p1 == uuid.Nil {
|
||||||
|
s.p1 = id
|
||||||
|
s.pMap[id] = game.SentinalID
|
||||||
|
return SessionRespJoined1
|
||||||
|
} else if s.p2 == uuid.Nil {
|
||||||
|
s.p2 = id
|
||||||
|
s.pMap[id] = game.ScourgeID
|
||||||
|
return SessionRespJoined2
|
||||||
|
} else {
|
||||||
|
return SessionRespJoinError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) Leave(id uuid.UUID) {
|
||||||
|
if id == s.p1 {
|
||||||
|
s.p1 = uuid.Nil
|
||||||
|
} else if id == s.p2 {
|
||||||
|
s.p2 = uuid.Nil
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
s.Game.Status = game.StatusStop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) Play(id uuid.UUID, cmd *game.Command) *game.CommandResult {
|
||||||
|
if s.pMap[id] != cmd.PlayerID {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return s.Game.Parse(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SessionCommand struct {
|
||||||
|
ID uuid.UUID `json:"player_id"`
|
||||||
|
Command SessionCmd `json:"command"`
|
||||||
|
GameCommand *game.Command `json:"game_command"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SessionCommand) String() string {
|
||||||
|
return fmt.Sprintf("%v %v\n%v\n", s.ID, s.Command, s.GameCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SessionCommandResult struct {
|
||||||
|
ID uuid.UUID `json:"player_id"`
|
||||||
|
Result SessionResp `json:"result"`
|
||||||
|
GameResult *game.CommandResult `json:"game_result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SessionCommandResult) String() string {
|
||||||
|
return fmt.Sprintf("%v %v\n%v\n", s.ID, s.Result, s.GameResult)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user