server/player.go

220 lines
4.7 KiB
Go
Raw Normal View History

2014-04-23 14:28:13 -07:00
package server
2013-08-19 20:43:26 -07:00
import (
"encoding/gob"
"encoding/json"
2013-08-19 20:43:26 -07:00
"log"
"bitbucket.org/smcquay/bandwidth"
"golang.org/x/net/websocket"
2013-08-19 20:43:26 -07:00
)
2014-02-16 23:59:17 -08:00
const maxMessageSize = 1024
type encoder interface {
Encode(v interface{}) error
}
type decoder interface {
Decode(v interface{}) error
}
2014-04-14 00:26:41 -07:00
// StreamCount is the wrapper we use around reads and writes to keep track of
// bandwidths.
type StreamCount struct {
ws *websocket.Conn
bw *bandwidth.Bandwidth
}
2014-04-14 00:26:41 -07:00
// 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
}
2014-04-14 00:26:41 -07:00
// 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
}
2014-04-14 00:26:41 -07:00
// Close is for cleanup
func (sc *StreamCount) Close() error {
return sc.ws.Close()
}
2014-04-14 00:26:41 -07:00
// 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
2014-04-14 00:26:41 -07:00
counter *StreamCount
send chan Message
Id string
2013-08-19 20:43:26 -07:00
}
2014-04-14 00:26:41 -07:00
func NewProtoTalker(id string, ws *websocket.Conn, bw *bandwidth.Bandwidth, encoding string) *ProtoTalker {
var enc encoder
var dec decoder
2014-04-14 00:26:41 -07:00
comptroller := &StreamCount{
ws: ws,
bw: bw,
}
if encoding == "json" {
enc = json.NewEncoder(comptroller)
dec = json.NewDecoder(comptroller)
} else {
enc = gob.NewEncoder(comptroller)
dec = gob.NewDecoder(comptroller)
}
2014-04-14 00:26:41 -07:00
return &ProtoTalker{
send: make(chan Message, 16),
enc: enc,
dec: dec,
counter: comptroller,
Id: id,
2014-02-17 01:07:29 -08:00
}
}
2014-04-14 00:26:41 -07:00
// 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)
2013-08-19 20:43:26 -07:00
if err != nil {
2014-03-29 01:51:40 -07:00
log.Println(err)
2013-08-19 20:43:26 -07:00
break
}
}
pt.counter.Close()
2014-04-14 00:26:41 -07:00
log.Printf("%s: Sender close", pt.Id)
}
2014-04-14 00:26:41 -07:00
// 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
2014-04-14 00:26:41 -07:00
ProtoTalker
2013-08-19 20:43:26 -07:00
}
2014-04-14 00:26:41 -07:00
func NewPlayer(id string, ws *websocket.Conn, bw *bandwidth.Bandwidth, encoding string) *Player {
return &Player{
2014-02-17 01:07:29 -08:00
Robots: []*Robot{},
2014-04-14 00:26:41 -07:00
ProtoTalker: *NewProtoTalker(id, ws, bw, encoding),
2014-02-17 01:07:29 -08:00
}
}
2014-04-14 00:26:41 -07:00
// 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")
2013-08-19 20:43:26 -07:00
for {
var msgs map[string]Instruction
err := p.dec.Decode(&msgs)
2014-02-16 23:59:17 -08:00
if err != nil {
log.Printf("%s: %s", p.Id, err)
2013-08-19 20:43:26 -07:00
break
}
2013-11-08 21:25:42 -08:00
for _, r := range p.Robots {
2013-11-08 22:26:56 -08:00
msg, ok := msgs[r.Id]
2013-11-08 21:25:42 -08:00
if !ok {
continue
2013-11-06 22:50:59 -08:00
}
2013-11-08 21:25:42 -08:00
if msg.Repair != nil && *msg.Repair == true {
r.ActiveScan = false
2013-11-08 21:25:42 -08:00
r.TargetSpeed = 0
r.FireAt = nil
r.MoveTo = nil
if r.RepairCounter <= 0 {
r.RepairCounter = 3.0
}
} else if msg.Scan != nil && *msg.Scan == true {
r.RepairCounter = 0
2013-11-08 21:25:42 -08:00
r.TargetSpeed = 0
r.FireAt = nil
r.MoveTo = nil
r.ActiveScan = true
2013-11-06 22:50:59 -08:00
} else {
2013-11-08 21:25:42 -08:00
r.RepairCounter = 0
r.ActiveScan = false
// Reapiring halts all other activity
if msg.MoveTo != nil {
r.MoveTo = msg.MoveTo
}
if msg.Heading != nil {
r.DesiredHeading = msg.Heading
}
2013-11-08 21:25:42 -08:00
if msg.FireAt != nil {
r.FireAt = msg.FireAt
2013-11-27 08:55:51 -08:00
} else {
r.FireAt = nil
2013-11-08 21:25:42 -08:00
}
if msg.TargetSpeed != nil {
2014-04-26 14:56:12 -07:00
r.TargetSpeed = *msg.TargetSpeed
2013-11-08 21:25:42 -08:00
} else {
r.TargetSpeed = r.Stats.Speed
}
}
2013-11-08 21:25:42 -08:00
if msg.Probe != nil {
r.Probe = msg.Probe
r.ProbeResult = nil
} else {
r.Probe = nil
2013-11-06 22:50:59 -08:00
}
2013-11-06 23:26:29 -08:00
2013-11-08 21:25:42 -08:00
if msg.Message != nil {
r.Message = *msg.Message
2013-11-06 23:26:29 -08:00
}
2013-08-19 20:43:26 -07:00
}
}
2013-11-08 21:25:42 -08:00
2014-04-14 00:26:41 -07:00
log.Printf("%s: Recv close", p.Id)
p.counter.Close()
2013-08-19 20:43:26 -07:00
}
2014-04-14 00:26:41 -07:00
// Spectator merely sends out game state, does not receive meaningful
// instructions from spectators.
type Spectator struct {
2014-04-14 00:26:41 -07:00
ProtoTalker
}
func NewSpectator(id string, ws *websocket.Conn, bw *bandwidth.Bandwidth, encoding string) *Spectator {
2014-02-17 01:07:29 -08:00
return &Spectator{
2014-04-14 00:26:41 -07:00
ProtoTalker: *NewProtoTalker(id, ws, bw, encoding),
2014-02-17 01:07:29 -08:00
}
}
2014-04-14 00:26:41 -07:00
// 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 map[string]Instruction
err := s.dec.Decode(&msgs)
if err != nil {
log.Printf("%s: %s", s.Id, err)
break
}
// After the first bit of handshaking, the rest of the messages should
// only be "{}" for spectators, and the following could hold true:
//
// if string(buff[:n]) != "{}" {
// log.Printf("protocol breach!!")
// break
// }
}
2014-04-14 00:26:41 -07:00
log.Printf("%s: Recv close", s.Id)
s.counter.Close()
}