package main import ( "flag" "log" "os" "os/signal" "strconv" "strings" "time" "unicode/utf8" "git.saintnet.tech/tomecraft/tome_lib" "github.com/google/uuid" "github.com/gorilla/websocket" ) var done chan interface{} var interrupt chan os.Signal var pid int var matchID uuid.UUID func receiveHandler(connection *websocket.Conn, container *UIContainer) { defer close(done) for { var resp tome_lib.SessionCommandResult err := connection.ReadJSON(&resp) if err != nil { log.Println("Error in receive:", err) break } switch resp.Result { case tome_lib.SessionRespJoined1: pid = tome_lib.SentinalID container.Output <- "joined as sentinal" case tome_lib.SessionRespJoined2: pid = tome_lib.ScourgeID container.Output <- "joined as scourge" case tome_lib.SessionRespFound: matchID = resp.MatchID container.Output <- "game found" case tome_lib.SessionRespPlayed: if resp.GameResult != nil { switch resp.GameResult.ResultType { case tome_lib.ActCmd: container.Output <- resp.GameResult.ActionResult.String() case tome_lib.StateCmd: container.State = resp.GameResult.StateResult container.Updated <- true case tome_lib.DebugCmd: container.Output <- "debug response" } } else { container.Output <- "error playing" } case tome_lib.SessionRespLeft: container.Output <- "game left" matchID = uuid.Nil return case tome_lib.SessionRespBroadcastSenTurn: container.Output <- "Sentinal may take their turn" case tome_lib.SessionRespBroadcastScoTrun: container.Output <- "Scourge may take their turn" case tome_lib.SessionRespBroadcastSenWin: container.Output <- "Sentinal wins!" case tome_lib.SessionRespBroadcastScoWin: container.Output <- "Scourge wins!" case tome_lib.SessionRespBroadcastUpdate: container.GetUpdate <- true case tome_lib.SessionRespBroadcastScoJoin: container.Output <- "Scourge has joined the game" case tome_lib.SessionRespBroadcastSenJoin: container.Output <- "Sentinal has joined the game" case tome_lib.SessionRespBroadcastScoLeft: container.Output <- "Scourge has left the game" case tome_lib.SessionRespBroadcastSenLeft: container.Output <- "Sentinal has left the game" case tome_lib.SessionRespBroadcastSenReady: container.Output <- "Sentinal is ready" case tome_lib.SessionRespBroadcastScoReady: container.Output <- "scourge is ready" case tome_lib.SessionRespBroadcastNone: case tome_lib.SessionRespError: container.Output <- "generic error" default: container.Output <- "Received a server response we don't know how to handle" } } } 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 tome_lib.SessionCommand) signal.Notify(interrupt, os.Interrupt) // Notify the interrupt channel for SIGINT hostname := flag.String("host", "localhost", "server hostname to connect to") port := flag.Int("port", 7636, "port to connect to") flag.Parse() port_s := strconv.Itoa(*port) socketUrl := "ws://" + *hostname + ":" + port_s + "/ws" 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, 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 tome_lib.SessionCommand select { case c = <-cmd: err := conn.WriteJSON(c) if err != nil { log.Println("Error during writing to websocket:", err) return } if c.Command == tome_lib.SessionCmdLeave { break } case <-container.GetUpdate: if container.State != nil { err = conn.WriteJSON(&tome_lib.SessionCommand{ ID: id, MatchID: matchID, Command: tome_lib.SessionCmdPlay, GameCommand: &tome_lib.Command{ PlayerID: pid, Type: tome_lib.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") // Close our websocket connection err := conn.WriteJSON(tome_lib.SessionCommand{ ID: id, Command: tome_lib.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 case <-ticker.C: select { case <-done: return default: if matchID != uuid.Nil { err := conn.WriteJSON(tome_lib.SessionCommand{ ID: id, MatchID: matchID, Command: tome_lib.SessionCmdPoll, }) if err != nil { log.Println("Error writing to websocket:", err) return } } } } } } func GetCommand(uid uuid.UUID, resp chan tome_lib.SessionCommand, container *UIContainer) { for { var cmd string var t int input := <-container.Input input_s := strings.Split(input, " ") t, err := strconv.Atoi(input_s[0]) if err != nil { continue } cmd = trimFirstRune(input) cmd = strings.TrimSpace(cmd) switch t { case 0: //session switch tome_lib.SessionCmd(cmd) { case tome_lib.SessionCmdQuery: resp <- tome_lib.SessionCommand{ ID: uid, Command: tome_lib.SessionCmdQuery, } case tome_lib.SessionCmdJoin: resp <- tome_lib.SessionCommand{ ID: uid, MatchID: matchID, Command: tome_lib.SessionCmdJoin, } case tome_lib.SessionCmdLeave: resp <- tome_lib.SessionCommand{ ID: uid, MatchID: matchID, Command: tome_lib.SessionCmdLeave, } default: break } case 1: //state resp <- tome_lib.SessionCommand{ ID: uid, MatchID: matchID, Command: tome_lib.SessionCmdPlay, GameCommand: &tome_lib.Command{ PlayerID: pid, Type: tome_lib.StateCmd, Cmd: cmd, }, } case 2: //action resp <- tome_lib.SessionCommand{ ID: uid, MatchID: matchID, Command: tome_lib.SessionCmdPlay, GameCommand: &tome_lib.Command{ PlayerID: pid, Type: tome_lib.ActCmd, Cmd: cmd, }, } } } } func trimFirstRune(s string) string { _, i := utf8.DecodeRuneInString(s) return s[i:] }