...

Source file src/code.rocket9labs.com/tslocum/bgammon/pkg/server/client.go

Documentation: code.rocket9labs.com/tslocum/bgammon/pkg/server

     1  package server
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"log"
     8  	"strconv"
     9  	"time"
    10  
    11  	"code.rocket9labs.com/tslocum/bgammon"
    12  )
    13  
    14  type clientRating struct {
    15  	backgammonSingle int
    16  	backgammonMulti  int
    17  	aceySingle       int
    18  	aceyMulti        int
    19  	tabulaSingle     int
    20  	tabulaMulti      int
    21  }
    22  
    23  func (r *clientRating) getRating(variant int8, multiPoint bool) int {
    24  	switch variant {
    25  	case bgammon.VariantBackgammon:
    26  		if !multiPoint {
    27  			return r.backgammonSingle
    28  		}
    29  		return r.backgammonMulti
    30  	case bgammon.VariantAceyDeucey:
    31  		if !multiPoint {
    32  			return r.aceySingle
    33  		}
    34  		return r.aceyMulti
    35  	case bgammon.VariantTabula:
    36  		if !multiPoint {
    37  			return r.tabulaSingle
    38  		}
    39  		return r.tabulaMulti
    40  	default:
    41  		log.Panicf("unknown variant: %d", variant)
    42  		return 0
    43  	}
    44  }
    45  
    46  func (r *clientRating) setRating(variant int8, multiPoint bool, rating int) {
    47  	switch variant {
    48  	case bgammon.VariantBackgammon:
    49  		if !multiPoint {
    50  			r.backgammonSingle = rating
    51  			return
    52  		}
    53  		r.backgammonMulti = rating
    54  	case bgammon.VariantAceyDeucey:
    55  		if !multiPoint {
    56  			r.aceySingle = rating
    57  			return
    58  		}
    59  		r.aceyMulti = rating
    60  	case bgammon.VariantTabula:
    61  		if !multiPoint {
    62  			r.tabulaSingle = rating
    63  		}
    64  		r.tabulaMulti = rating
    65  	default:
    66  		log.Panicf("unknown variant: %d", variant)
    67  	}
    68  }
    69  
    70  type serverClient struct {
    71  	id           int
    72  	json         bool
    73  	name         []byte
    74  	account      *account
    75  	accountID    int
    76  	connected    int64
    77  	active       int64
    78  	lastPing     int64
    79  	commands     chan []byte
    80  	autoplay     bool
    81  	playerNumber int8
    82  	terminating  bool
    83  	bgammon.Client
    84  }
    85  
    86  func (c *serverClient) sendEvent(e interface{}) {
    87  	// JSON formatted messages.
    88  	if c.json {
    89  		switch ev := e.(type) {
    90  		case *bgammon.EventWelcome:
    91  			ev.Type = bgammon.EventTypeWelcome
    92  		case *bgammon.EventHelp:
    93  			ev.Type = bgammon.EventTypeHelp
    94  		case *bgammon.EventPing:
    95  			ev.Type = bgammon.EventTypePing
    96  		case *bgammon.EventNotice:
    97  			ev.Type = bgammon.EventTypeNotice
    98  		case *bgammon.EventSay:
    99  			ev.Type = bgammon.EventTypeSay
   100  		case *bgammon.EventList:
   101  			ev.Type = bgammon.EventTypeList
   102  		case *bgammon.EventJoined:
   103  			ev.Type = bgammon.EventTypeJoined
   104  		case *bgammon.EventFailedJoin:
   105  			ev.Type = bgammon.EventTypeFailedJoin
   106  		case *bgammon.EventLeft:
   107  			ev.Type = bgammon.EventTypeLeft
   108  		case *bgammon.EventFailedLeave:
   109  			ev.Type = bgammon.EventTypeFailedLeave
   110  		case *bgammon.EventBoard:
   111  			ev.Type = bgammon.EventTypeBoard
   112  		case *bgammon.EventRolled:
   113  			ev.Type = bgammon.EventTypeRolled
   114  		case *bgammon.EventFailedRoll:
   115  			ev.Type = bgammon.EventTypeFailedRoll
   116  		case *bgammon.EventMoved:
   117  			ev.Type = bgammon.EventTypeMoved
   118  		case *bgammon.EventFailedMove:
   119  			ev.Type = bgammon.EventTypeFailedMove
   120  		case *bgammon.EventFailedOk:
   121  			ev.Type = bgammon.EventTypeFailedOk
   122  		case *bgammon.EventWin:
   123  			ev.Type = bgammon.EventTypeWin
   124  		case *bgammon.EventSettings:
   125  			ev.Type = bgammon.EventTypeSettings
   126  		case *bgammon.EventReplay:
   127  			ev.Type = bgammon.EventTypeReplay
   128  		case *bgammon.EventHistory:
   129  			ev.Type = bgammon.EventTypeHistory
   130  		default:
   131  			log.Panicf("unknown event type %+v", ev)
   132  		}
   133  
   134  		buf, err := json.Marshal(e)
   135  		if err != nil {
   136  			panic(err)
   137  		}
   138  		c.Write(buf)
   139  		return
   140  	}
   141  
   142  	// Human-readable messages.
   143  	switch ev := e.(type) {
   144  	case *bgammon.EventWelcome:
   145  		c.Write([]byte(fmt.Sprintf("welcome %s there are %d clients playing %d matches.", ev.PlayerName, ev.Clients, ev.Games)))
   146  	case *bgammon.EventHelp:
   147  		c.Write([]byte("helpstart Help text:"))
   148  		c.Write([]byte(fmt.Sprintf("help %s", ev.Message)))
   149  		c.Write([]byte("helpend End of help text."))
   150  	case *bgammon.EventPing:
   151  		c.Write([]byte(fmt.Sprintf("ping %s", ev.Message)))
   152  	case *bgammon.EventNotice:
   153  		c.Write([]byte(fmt.Sprintf("notice %s", ev.Message)))
   154  	case *bgammon.EventSay:
   155  		c.Write([]byte(fmt.Sprintf("say %s %s", ev.Player, ev.Message)))
   156  	case *bgammon.EventList:
   157  		c.Write([]byte("liststart Matches list:"))
   158  		for _, g := range ev.Games {
   159  			password := 0
   160  			if g.Password {
   161  				password = 1
   162  			}
   163  			name := "(No name)"
   164  			if g.Name != "" {
   165  				name = g.Name
   166  			}
   167  			c.Write([]byte(fmt.Sprintf("game %d %d %d %d %s", g.ID, password, g.Points, g.Players, name)))
   168  		}
   169  		c.Write([]byte("listend End of matches list."))
   170  	case *bgammon.EventJoined:
   171  		c.Write([]byte(fmt.Sprintf("joined %d %d %s", ev.GameID, ev.PlayerNumber, ev.Player)))
   172  	case *bgammon.EventFailedJoin:
   173  		c.Write([]byte(fmt.Sprintf("failedjoin %s", ev.Reason)))
   174  	case *bgammon.EventLeft:
   175  		c.Write([]byte(fmt.Sprintf("left %s", ev.Player)))
   176  	case *bgammon.EventRolled:
   177  		msg := []byte(fmt.Sprintf("rolled %s %d %d", ev.Player, ev.Roll1, ev.Roll2))
   178  		if ev.Roll3 != 0 {
   179  			msg = append(msg, []byte(fmt.Sprintf(" %d", ev.Roll3))...)
   180  		}
   181  		c.Write(msg)
   182  	case *bgammon.EventFailedRoll:
   183  		c.Write([]byte(fmt.Sprintf("failedroll %s", ev.Reason)))
   184  	case *bgammon.EventMoved:
   185  		c.Write([]byte(fmt.Sprintf("moved %s %s", ev.Player, bgammon.FormatAndFlipMoves(ev.Moves, c.playerNumber, bgammon.VariantBackgammon))))
   186  	case *bgammon.EventFailedMove:
   187  		c.Write([]byte(fmt.Sprintf("failedmove %d/%d %s", ev.From, ev.To, ev.Reason)))
   188  	case *bgammon.EventFailedOk:
   189  		c.Write([]byte(fmt.Sprintf("failedok %s", ev.Reason)))
   190  	case *bgammon.EventWin:
   191  		if ev.Points != 0 {
   192  			c.Write([]byte(fmt.Sprintf("win %s wins %d points!", ev.Player, ev.Points)))
   193  		} else {
   194  			c.Write([]byte(fmt.Sprintf("win %s wins!", ev.Player)))
   195  		}
   196  	default:
   197  		log.Printf("warning: skipped sending unknown event to non-json client: %+v", ev)
   198  	}
   199  }
   200  
   201  func (c *serverClient) sendNotice(message string) {
   202  	c.sendEvent(&bgammon.EventNotice{
   203  		Message: message,
   204  	})
   205  }
   206  
   207  func (c *serverClient) label() string {
   208  	if len(c.name) > 0 {
   209  		return string(c.name)
   210  	}
   211  	return strconv.Itoa(c.id)
   212  }
   213  
   214  func (c *serverClient) Terminate(reason string) {
   215  	if c.Terminated() || c.terminating {
   216  		return
   217  	}
   218  	c.terminating = true
   219  
   220  	var extra string
   221  	if reason != "" {
   222  		extra = ": " + reason
   223  	}
   224  	c.sendNotice("Connection terminated" + extra)
   225  
   226  	go func() {
   227  		time.Sleep(time.Second)
   228  		c.Client.Terminate(reason)
   229  	}()
   230  }
   231  
   232  func logClientRead(msg []byte) {
   233  	msgLower := bytes.ToLower(msg)
   234  	loginJSON := bytes.HasPrefix(msgLower, []byte("loginjson ")) || bytes.HasPrefix(msgLower, []byte("lj "))
   235  	if bytes.HasPrefix(msgLower, []byte("login ")) || bytes.HasPrefix(msgLower, []byte("l ")) || loginJSON {
   236  		split := bytes.Split(msg, []byte(" "))
   237  		var clientName []byte
   238  		var username []byte
   239  		var password []byte
   240  		l := len(split)
   241  		if l > 1 {
   242  			if loginJSON {
   243  				clientName = split[1]
   244  			} else {
   245  				username = split[1]
   246  			}
   247  			if l > 2 {
   248  				if loginJSON {
   249  					username = split[2]
   250  				} else {
   251  					password = []byte("*******")
   252  				}
   253  				if l > 3 {
   254  					if loginJSON {
   255  						password = []byte("*******")
   256  					}
   257  				}
   258  			}
   259  		}
   260  		if len(clientName) == 0 {
   261  			clientName = []byte("unspecified")
   262  		}
   263  		log.Printf("<- %s %s %s %s", split[0], clientName, username, password)
   264  	} else if !bytes.HasPrefix(msgLower, []byte("list")) && !bytes.HasPrefix(msgLower, []byte("ls")) && !bytes.HasPrefix(msgLower, []byte("pong")) {
   265  		log.Printf("<- %s", msg)
   266  	}
   267  }
   268  

View as plain text