You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
216 lines
5.0 KiB
216 lines
5.0 KiB
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 |
|
}
|
|
|