|
|
|
@ -19,39 +19,46 @@ type decoder interface {
|
|
|
|
|
Decode(v interface{}) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type streamCounter struct {
|
|
|
|
|
// StreamCount is the wrapper we use around reads and writes to keep track of
|
|
|
|
|
// bandwidths.
|
|
|
|
|
type StreamCount struct {
|
|
|
|
|
ws *websocket.Conn
|
|
|
|
|
bw *bandwidth.Bandwidth
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (sc *streamCounter) Read(p []byte) (n int, err error) {
|
|
|
|
|
// StreamCount.Read implements io.Reader
|
|
|
|
|
func (sc *StreamCount) Read(p []byte) (n int, err error) {
|
|
|
|
|
n, err = sc.ws.Read(p)
|
|
|
|
|
sc.bw.AddRx <- n
|
|
|
|
|
return n, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (sc *streamCounter) Write(p []byte) (n int, err error) {
|
|
|
|
|
// StreamCount.Write implements io.Writer
|
|
|
|
|
func (sc *StreamCount) Write(p []byte) (n int, err error) {
|
|
|
|
|
n, err = sc.ws.Write(p)
|
|
|
|
|
sc.bw.AddTx <- n
|
|
|
|
|
return n, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (sc *streamCounter) Close() error {
|
|
|
|
|
// Close is for cleanup
|
|
|
|
|
func (sc *StreamCount) Close() error {
|
|
|
|
|
return sc.ws.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type protoTalker struct {
|
|
|
|
|
// ProtoTalker is the simplest form of struct that talks to consumers of the
|
|
|
|
|
// service. There are two important methods here: Sender and Recv.
|
|
|
|
|
type ProtoTalker struct {
|
|
|
|
|
enc encoder
|
|
|
|
|
dec decoder
|
|
|
|
|
counter *streamCounter
|
|
|
|
|
counter *StreamCount
|
|
|
|
|
send chan Message
|
|
|
|
|
Id string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewProtoTalker(id string, ws *websocket.Conn, bw *bandwidth.Bandwidth, encoding string) *protoTalker {
|
|
|
|
|
func NewProtoTalker(id string, ws *websocket.Conn, bw *bandwidth.Bandwidth, encoding string) *ProtoTalker {
|
|
|
|
|
var enc encoder
|
|
|
|
|
var dec decoder
|
|
|
|
|
comptroller := &streamCounter{
|
|
|
|
|
comptroller := &StreamCount{
|
|
|
|
|
ws: ws,
|
|
|
|
|
bw: bw,
|
|
|
|
|
}
|
|
|
|
@ -62,7 +69,7 @@ func NewProtoTalker(id string, ws *websocket.Conn, bw *bandwidth.Bandwidth, enco
|
|
|
|
|
enc = gob.NewEncoder(comptroller)
|
|
|
|
|
dec = gob.NewDecoder(comptroller)
|
|
|
|
|
}
|
|
|
|
|
return &protoTalker{
|
|
|
|
|
return &ProtoTalker{
|
|
|
|
|
send: make(chan Message, 16),
|
|
|
|
|
enc: enc,
|
|
|
|
|
dec: dec,
|
|
|
|
@ -71,8 +78,10 @@ func NewProtoTalker(id string, ws *websocket.Conn, bw *bandwidth.Bandwidth, enco
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (pt *protoTalker) sender() {
|
|
|
|
|
log.Printf("%s: %T sender launched", pt.Id, pt.enc)
|
|
|
|
|
// Sender is the single implementation for data output to clients, both players
|
|
|
|
|
// and spectators.
|
|
|
|
|
func (pt *ProtoTalker) Sender() {
|
|
|
|
|
log.Printf("%s: %T Sender launched", pt.Id, pt.enc)
|
|
|
|
|
for things := range pt.send {
|
|
|
|
|
err := pt.enc.Encode(things)
|
|
|
|
|
if err != nil {
|
|
|
|
@ -81,24 +90,28 @@ func (pt *protoTalker) sender() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pt.counter.Close()
|
|
|
|
|
log.Printf("%s: sender close", pt.Id)
|
|
|
|
|
log.Printf("%s: Sender close", pt.Id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type player struct {
|
|
|
|
|
// player uses protoTalker's Sender method, but adds a Recv that knows how to
|
|
|
|
|
// deal with game play instructions from the player.
|
|
|
|
|
type Player struct {
|
|
|
|
|
Robots []*Robot
|
|
|
|
|
Instruction Instruction
|
|
|
|
|
protoTalker
|
|
|
|
|
ProtoTalker
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewPlayer(id string, ws *websocket.Conn, bw *bandwidth.Bandwidth, encoding string) *player {
|
|
|
|
|
return &player{
|
|
|
|
|
func NewPlayer(id string, ws *websocket.Conn, bw *bandwidth.Bandwidth, encoding string) *Player {
|
|
|
|
|
return &Player{
|
|
|
|
|
Robots: []*Robot{},
|
|
|
|
|
protoTalker: *NewProtoTalker(id, ws, bw, encoding),
|
|
|
|
|
ProtoTalker: *NewProtoTalker(id, ws, bw, encoding),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *player) recv() {
|
|
|
|
|
log.Println("starting recv")
|
|
|
|
|
// Player.Recv is the function responsible for parsing out player instructions
|
|
|
|
|
// and sending them to the game.
|
|
|
|
|
func (p *Player) Recv() {
|
|
|
|
|
log.Println("starting Recv")
|
|
|
|
|
for {
|
|
|
|
|
var msgs map[string]Instruction
|
|
|
|
|
err := p.dec.Decode(&msgs)
|
|
|
|
@ -165,21 +178,26 @@ func (p *player) recv() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Printf("%s: recv close", p.Id)
|
|
|
|
|
log.Printf("%s: Recv close", p.Id)
|
|
|
|
|
p.counter.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Spectator merely sends out game state, does not receive meaningful
|
|
|
|
|
// instructions from spectators.
|
|
|
|
|
type Spectator struct {
|
|
|
|
|
protoTalker
|
|
|
|
|
ProtoTalker
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewSpectator(id string, ws *websocket.Conn, bw *bandwidth.Bandwidth, encoding string) *Spectator {
|
|
|
|
|
return &Spectator{
|
|
|
|
|
protoTalker: *NewProtoTalker(id, ws, bw, encoding),
|
|
|
|
|
ProtoTalker: *NewProtoTalker(id, ws, bw, encoding),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Spectator) recv() {
|
|
|
|
|
// Spectator.Recv is an interesting beast. We had to add it as for whatever
|
|
|
|
|
// reason the server would lock up if we weren't reading the empty responses
|
|
|
|
|
// from spectators.
|
|
|
|
|
func (s *Spectator) Recv() {
|
|
|
|
|
for {
|
|
|
|
|
var msgs interface{}
|
|
|
|
|
err := s.dec.Decode(&msgs)
|
|
|
|
@ -196,6 +214,6 @@ func (s *Spectator) recv() {
|
|
|
|
|
// break
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
log.Printf("%s: recv close", s.Id)
|
|
|
|
|
log.Printf("%s: Recv close", s.Id)
|
|
|
|
|
s.counter.Close()
|
|
|
|
|
}
|
|
|
|
|