aboutsummaryrefslogtreecommitdiff
path: root/uis
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fl.fischer@fau.de>2017-02-19 22:22:46 +0100
committerFlorian Fischer <florian.fl.fischer@fau.de>2017-02-21 15:20:22 +0100
commitb8bdd9d0419bab8ee0a7dd4b6e9fb1df7fecf221 (patch)
tree392dee0e299be6b6370ba911341fd786fa9111f1 /uis
parentf08b6e39f4157dc209e30407909b879caf6760e0 (diff)
downloadgoffel-b8bdd9d0419bab8ee0a7dd4b6e9fb1df7fecf221.tar.gz
goffel-b8bdd9d0419bab8ee0a7dd4b6e9fb1df7fecf221.zip
move network code from uis to net
logic: add NewPlayer function
Diffstat (limited to 'uis')
-rw-r--r--uis/client.go12
-rw-r--r--uis/interactive.go2
-rw-r--r--uis/server.go489
3 files changed, 1 insertions, 502 deletions
diff --git a/uis/client.go b/uis/client.go
deleted file mode 100644
index beca3a6..0000000
--- a/uis/client.go
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) 2016 Florian Fischer. All rights reserved.
-// Use of this source code is governed by a MIT license found in the LICENSE file.
-
-package uis
-
-type Client struct {
- Port string
-}
-
-func (ui *Client) Run() {
- println("Not Implemented")
-}
diff --git a/uis/interactive.go b/uis/interactive.go
index 8210122..5e7994e 100644
--- a/uis/interactive.go
+++ b/uis/interactive.go
@@ -36,7 +36,7 @@ func (i *Interactive) init() {
if err != nil {
break
}
- i.players = append(i.players, Player{name, NewScore()})
+ i.players = append(i.players, NewPlayer(name))
count++
}
diff --git a/uis/server.go b/uis/server.go
deleted file mode 100644
index 220699c..0000000
--- a/uis/server.go
+++ /dev/null
@@ -1,489 +0,0 @@
-// Copyright (c) 2016 Florian Fischer. All rights reserved.
-// Use of this source code is governed by a MIT license found in the LICENSE file.
-
-package uis
-
-import (
- "bufio"
- "encoding/json"
- "errors"
- "fmt"
- "net"
- "strconv"
- "strings"
- "sync"
-
- "muhq.space/go/muhq/goffel/logic"
-)
-
-var (
- // 0-9 Misc
- // 10-19 Sucess
- // 20-29 IO Errors
- // 30 Command Errors
- // 90-99 Session Responses
- responsePlain = map[int]string{
- 0: "Success: ",
- 1: "Welcome ",
- 2: "Sessions:",
- 11: "Help:\r\n",
- 12: "Score:\r\n",
- 13: "Dice: ",
- 14: "Insert: ",
- 15: "Cancel: ",
- 16: "Reroll: ",
- 20: "I/O Error: ",
- 21: "Json Decoding Error: ",
- 30: "Invalid Command: ",
- 31: "Insert Error: ",
- 32: "Cancel Error: ",
- 33: "Reroll Error: ",
- 90: "Connected To: ",
- 91: "To Many Players: ",
- 92: "New Players: ",
- 93: "Starting Game: ",
- 94: "Round: ",
- 95: "", // Used to forward results of other players
- 96: "Remove Client: ",
- 97: "Winners: ",
- }
-)
-
-// Internal structure representing a server response
-type sMsg struct {
- Response int
- Payload interface{}
-}
-
-func (msg sMsg) String() string {
- response := responsePlain[msg.Response]
- payload := msg.Payload
- res := ""
- if msg.Response == 2 {
- sessionsptr, _ := msg.Payload.(*map[string]*session)
- sessions := *sessionsptr
- s := ""
- for name, session := range sessions {
- s += fmt.Sprintf("\r\n%s: %v", name, session)
- }
- payload = s
- }
- if msg.Response == 11 {
- a, _ := msg.Payload.([]string)
- payload = strings.Join(a, "\r\n")
- }
- if msg.Response == 95 {
- a, _ := msg.Payload.([]interface{})
- res, _ = a[0].(string)
- msg, _ = a[1].(sMsg)
- res += " " + responsePlain[msg.Response]
- }
- if msg.Response == 14 {
- a, _ := msg.Payload.([]interface{})
- c, _ := a[0].(logic.Cmd)
- payload = fmt.Sprintf("%d -> %v", a[1], logic.ScoreNames[c.Argv[0]])
- } else if msg.Response == 15 {
- a, _ := msg.Payload.([]interface{})
- c, _ := a[0].(logic.Cmd)
- payload = fmt.Sprintf("%v", logic.ScoreNames[c.Argv[0]])
- }
- return fmt.Sprintf("%s%s%v", response, res, payload)
-}
-
-type player struct {
- logic.Player
- client *client
-}
-
-func (p player) String() string {
- return p.Name
-}
-
-type client struct {
- sock net.Conn
- reader *bufio.Reader
- writer *bufio.Writer
- encoder *json.Encoder
- decoder *json.Decoder
- encoding string
- players []player
- results chan sMsg
-}
-
-func (c *client) send(msg sMsg) error {
- var err error
- if c.encoding == "plain" {
- _, err = c.writer.WriteString(fmt.Sprintf("%v\r\n", msg))
- if err == nil {
- err = c.writer.Flush()
- }
- } else {
- err = c.encoder.Encode(msg)
- }
- return err
-}
-
-func (c *client) fail(res int, pl interface{}) {
- c.send(sMsg{res, pl})
- c.sock.Close()
-}
-
-func (c *client) recvCmd() (logic.Cmd, error) {
- var err error
- var cmd logic.Cmd
- if c.encoding == "plain" {
- input := ""
- input, err = c.reader.ReadString('\n')
- if err != nil {
- return cmd, err
- }
- cmd, err = logic.ParseCmd(input)
- } else {
- err = c.decoder.Decode(&cmd)
- }
- return cmd, err
-}
-
-func (c *client) getBest() ([]player, int) {
- best := []player{}
- max := 0
- for _, p := range c.players {
- t := p.Score.Score()
- if t > max {
- best = []player{p}
- max = t
- } else if t == max {
- best = append(best, p)
- }
- }
- return best, max
-}
-
-func (c *client) round(round int, wg *sync.WaitGroup) {
- dice := logic.Dice{}
- var msg sMsg
-
- for _, p := range c.players {
- fin := false
- dice.Roll(nil)
- cmd := logic.Cmd{}
- var err error
- rerolls := 0
- err = c.send(sMsg{94, []interface{}{round, p.Name, dice}})
- if err != nil {
- fin = true
- }
- for !fin {
- // clear message
- msg = sMsg{}
- cmd, err = c.recvCmd()
- if err != nil {
- msg.Response = 30
- msg.Payload = err
- }
-
- switch cmd.Cmd {
- case "h":
- msg.Response = 11
- msg.Payload = logic.CmdHelp(cmd.Argv[0])
- case "p":
- msg.Response = 12
- msg.Payload = p.Score
- case "d":
- msg.Response = 13
- msg.Payload = dice
- case "q":
- //client quits
- err = errors.New("Quit")
- c.sock.Close()
- case "i":
- i, err := p.Score.Insert(dice, cmd.Argv[0])
- if err != nil {
- msg.Response = 31
- msg.Payload = err
- } else {
- msg.Response = 14
- msg.Payload = []interface{}{cmd, i, p.Score}
- fin = true
- }
- case "c":
- err := p.Score.Cancel(cmd.Argv[0])
- if err != nil {
- msg.Response = 32
- msg.Payload = err
- } else {
- msg.Response = 15
- msg.Payload = []interface{}{cmd, p.Score}
- fin = true
- }
- case "r":
- var err error
- if rerolls < 2 {
- err = dice.Roll(cmd.Argv)
- } else {
- err = errors.New("Only two rerolls are allowed.")
- }
- if err != nil {
- msg.Response = 33
- msg.Payload = err
- } else {
- msg.Response = 16
- msg.Payload = dice
- rerolls++
- }
- default:
- }
- if err = c.send(msg); err != nil {
- fin = true
- }
- }
- if err != nil {
- msg = sMsg{96, err}
- } else {
- msg = sMsg{95, []interface{}{p.Name, msg}}
- }
- c.results <- msg
- }
- wg.Done()
-}
-
-type session struct {
- clients []*client
- maxPlayers int
- curPlayers int
- status string
-}
-
-func (s session) String() (ret string) {
- ret += s.status
- ret += " max:"
- ret += strconv.Itoa(s.maxPlayers)
- ret += " p:"
- names := make([]string, 0, s.maxPlayers)
- for _, c := range s.clients {
- for _, p := range c.players {
- names = append(names, p.Name)
- }
- }
- return fmt.Sprintf("%s%v", ret, names)
-}
-
-func (s *session) terminate(reason sMsg) {
- for _, c := range s.clients {
- noreason := sMsg{}
- if reason != noreason {
- c.send(reason)
- }
- c.sock.Close()
- }
- s.status = "TERMINATED"
-}
-
-func (s *session) addClient(c *client) {
- newNames := make([]string, 0, len(c.players))
- for _, n := range c.players {
- newNames = append(newNames, n.Name)
- }
- okClients := make([]*client, 0, len(c.players))
- for _, other := range s.clients {
- err := other.send(sMsg{92, newNames})
- if err == nil {
- okClients = append(okClients, other)
- } else {
- s.curPlayers--
- }
- }
- s.clients = okClients
- s.clients = append(s.clients, c)
- s.curPlayers += len(c.players)
- if s.curPlayers == s.maxPlayers {
- go s.play()
- }
-}
-
-func (s *session) play() {
- s.status = "PLAYING"
- msg := sMsg{93, ""}
- wg := sync.WaitGroup{}
- for _, c := range s.clients {
- c.send(msg)
- }
- var reason sMsg
- for round := 1; round < 14; round++ {
- wg.Add(len(s.clients))
- for _, c := range s.clients {
- go c.round(round, &wg)
- }
- // broadcast results
- wg.Wait()
- badClients := make(map[*client]bool)
- for i, c := range s.clients {
- for _, p := range c.players {
- res := <-c.results
- // collect broken clients
- if res.Response == 96 {
- badClients[c] = true
- res = sMsg{96, []interface{}{p.Name, res.Payload}}
- }
- for j, c2 := range s.clients {
- if i != j {
- // ignore error
- c2.send(res)
- }
- }
- }
- }
- // remove broken clients
- okClients := make([]*client, 0, len(s.clients)-len(badClients))
- for _, c := range s.clients {
- if _, bad := badClients[c]; bad == false {
- okClients = append(okClients, c)
- }
- }
- s.clients = okClients
- }
- winners := []player{}
- max := 0
- for _, c := range s.clients {
- best, m := c.getBest()
- if m > max {
- winners = best
- max = m
- } else if m == max {
- winners = append(winners, best...)
- }
- }
- msg = sMsg{97, []interface{}{max, winners}}
- for _, c := range s.clients {
- c.send(msg)
- }
- s.terminate(reason)
-}
-
-type Server struct {
- port string
- greeting string
- sessions map[string]*session
-}
-
-func NewServer(port string, greeting string) Server {
- return Server{port, greeting, map[string]*session{}}
-}
-
-func (s *Server) Serve() error {
- ln, err := net.Listen("tcp", ":"+s.port)
- if err != nil {
- return err
- }
- sessionLock := sync.Mutex{}
- for {
- sock, err := ln.Accept()
- if err != nil {
- return err
- }
- go func() {
- reader := bufio.NewReader(sock)
-
- input, err := reader.ReadString('\n')
- if err != nil {
- sock.Close()
- return
- }
- c := client{}
- c.sock = sock
-
- // is HELO json encoded ?
- var helo struct {
- Cmd string
- Players []string
- }
- err = json.Unmarshal([]byte(input), &helo)
- if err == nil {
- c.encoder = json.NewEncoder(sock)
- c.decoder = json.NewDecoder(sock)
- c.encoding = "json"
- if helo.Cmd != "HELO" {
- c.fail(30, "HELO expected")
- return
- }
- } else {
- c.reader = reader
- c.writer = bufio.NewWriter(sock)
- c.encoding = "plain"
-
- input = strings.TrimRight(input, " \r\n")
- line := strings.Split(input, " ")
- if line[0] != "HELO" {
- c.fail(30, "HELO expected")
- return
- } else if len(line) < 2 {
- c.fail(30, "player name expected")
- return
- }
- helo.Players = line[1:]
- }
- for _, name := range helo.Players {
- c.players = append(c.players, player{logic.Player{name, logic.NewScore()}, &c})
- }
- c.results = make(chan sMsg, len(c.players))
-
- c.send(sMsg{1, s.greeting})
- //names := []string{}
- //for name := range s.session {
- //names = append(names, name)
- //}
- //c.send(2, names)
- sessionLock.Lock()
- c.send(sMsg{2, &s.sessions})
- sessionLock.Unlock()
- var reg struct {
- Name string
- MaxPlayers int
- }
- if c.encoding == "json" {
- err = c.decoder.Decode(&reg)
- if err != nil {
- c.fail(21, err)
- return
- }
- } else {
- input, err = c.reader.ReadString('\n')
- if err != nil {
- c.fail(20, err)
- return
- }
- input = strings.TrimRight(input, " \r\n")
- line := strings.Split(input, " ")
- reg.Name = line[0]
- if len(line) > 1 {
- reg.MaxPlayers, err = strconv.Atoi(line[1])
- if err != nil {
- c.fail(30, err)
- return
- }
- }
- }
- if reg.Name == "" {
- c.fail(30, "Session name expected")
- return
- }
- if reg.MaxPlayers == 0 {
- reg.MaxPlayers = 2
- }
-
- sessionLock.Lock()
- defer sessionLock.Unlock()
- ses, exists := s.sessions[reg.Name]
- if !exists || ses.status == "TERMINATED" {
- ses = &session{[]*client{}, reg.MaxPlayers, 0, "OPEN"}
- s.sessions[reg.Name] = ses
- }
- if ses.curPlayers+len(c.players) > ses.maxPlayers {
- c.fail(91, ses.maxPlayers-ses.curPlayers)
- return
- }
- ses.addClient(&c)
- }()
- }
- return nil
-}