package main import ( "log" "net" "strconv" "strings" "sync" "github.com/gorilla/websocket" "gitlab.com/tslocum/joker" ) const ( ClientTerminating = -1 ClientTelnet = 1 ClientWebsocket = 2 ) const ( trimNewlinesAndSpace = " \r\n\x00" trimNewlines = "\r\n\x00" ) type Client struct { ConnType int ConnTelnet net.Conn ConnWebsocket *websocket.Conn game *Game gameplayer int gameready bool gamestatus string gamego bool readbuffer chan string writebuffer chan string sync.RWMutex } func (c *Client) initialize() { c.readbuffer = make(chan string, 10) c.writebuffer = make(chan string, 10) } func (c *Client) processRead() { var command_pieces []string var handled bool for command := range c.readbuffer { if command == "" || c.game == nil { break } else if c.ConnType == ClientTerminating { continue } handled = false command = strings.TrimLeft(command, trimNewlinesAndSpace) command = strings.TrimRight(command, trimNewlinesAndSpace) command_pieces = strings.Fields(strings.ToLower(command)) if len(command_pieces) > 0 { handled = true args := "" if len(command_pieces) > 1 { for i := 1; i < len(command_pieces); i++ { command_piece := command_pieces[i] command_piece = strings.TrimLeft(command_piece, trimNewlinesAndSpace) command_piece = strings.TrimRight(command_piece, trimNewlinesAndSpace) if command_piece != "" { if args != "" { args += " " } args += command_piece } } } card, sentCardIdentifier := joker.Parse(command_pieces[0]) if _, err := strconv.Atoi(command_pieces[0]); err == nil { c.game.CommandQueue <- GameCommand{Player: c.gameplayer, Command: CommandRaw, Value: command_pieces[0] + " " + args} } else if sentCardIdentifier && c.game.getHand(c.gameplayer).Contains(card) { c.game.CommandQueue <- GameCommand{Player: c.gameplayer, Command: CommandRaw, Value: command_pieces[0] + " " + args} } else if command_pieces[0] == "continue" { c.game.CommandQueue <- GameCommand{Player: c.gameplayer, Command: CommandContinue, Value: args} } else if command_pieces[0] == "cut" || command_pieces[0] == "c" || command_pieces[0] == "cu" { c.game.CommandQueue <- GameCommand{Player: c.gameplayer, Command: CommandCut, Value: args} } else if command_pieces[0] == "throw" || command_pieces[0] == "t" || command_pieces[0] == "th" { c.game.CommandQueue <- GameCommand{Player: c.gameplayer, Command: CommandThrow, Value: args} } else if command_pieces[0] == "message" || command_pieces[0] == "msg" || command_pieces[0] == "say" { c.game.CommandQueue <- GameCommand{Player: c.gameplayer, Command: CommandMessage, Value: args} } else if command_pieces[0] == "print" && c.game != nil { c.game.printAll() } else { handled = false } } if !handled { c.write(c.game.printGame(c.gameplayer)) } } } func (c *Client) read(message string) { if c.ConnType == ClientTerminating { return } c.readbuffer <- message } func (c *Client) handleRead() { if c.ConnType == ClientWebsocket { for { messageType, message, err := c.ConnWebsocket.ReadMessage() if err != nil { log.Println("WebSocket read error:", err) break } log.Printf("WebSocket read %d: %s", messageType, string(message)) c.readbuffer <- string(message) } } else { buf := make([]byte, 4096) var readtext string var nextline int for { n, err := c.ConnTelnet.Read(buf) if err != nil || n == 0 { c.ConnTelnet.Close() break } readtext += strings.TrimLeft(string(buf), trimNewlines) for { readtext = strings.TrimLeft(readtext, trimNewlines) nextline = strings.Index(readtext, "\n") if nextline <= 0 { if readtext != "" { c.read(readtext) readtext = "" } break } c.read(readtext[:nextline+1]) readtext = readtext[nextline+1:] } buf = make([]byte, 4096) } log.Printf("Telnet connection closed") } c.terminate() if c.game != nil && c.game.Phase != PhaseEnd { c.game.end(c.game.getOpponent(c.gameplayer), "Player disconnected") } } func (c *Client) terminate() { c.write("") c.ConnType = ClientTerminating c.readbuffer <- "" } func (c *Client) write(message string) { if c.ConnType == ClientTerminating { return } c.writebuffer <- message } func (c *Client) handleWrite() { for message := range c.writebuffer { if message == "" { break } else if c.ConnType == ClientTerminating { continue } if c.ConnType == ClientWebsocket { err := c.ConnWebsocket.WriteMessage(websocket.TextMessage, []byte(message)) if err != nil { log.Printf("WebSocket connection closed: %v", err) break } } else { _, err := c.ConnTelnet.Write([]byte(message + "\n\n")) if err != nil { c.ConnTelnet.Close() log.Printf("Telnet connection closed: %v", err) break } } } } func (c *Client) setStatus(status string) { c.Lock() defer c.Unlock() c.gamestatus = status } func (c *Client) getStatus() string { c.RLock() defer c.RUnlock() return c.gamestatus }