Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package main
- import (
- "encoding/binary"
- "image/color"
- "io"
- "log"
- "net"
- "sync"
- "time"
- )
- var colors = []color.RGBA{
- {253, 255, 252, 255},
- {46, 196, 182, 255},
- {231, 29, 54, 255},
- {255, 159, 28, 255},
- }
- type Player struct {
- Id byte
- conn net.Conn
- in chan []byte
- out chan []byte
- }
- type Event struct {
- Time time.Time
- Msg []byte
- }
- type Board struct {
- playerId byte
- players []*Player
- playersMu sync.Mutex
- history []Event
- historyMu sync.Mutex
- }
- func NewBoard() *Board {
- return &Board{}
- }
- func (b *Board) HandleConn(conn net.Conn) error {
- // Register player.
- b.playersMu.Lock()
- b.playerId++
- player := &Player{
- Id: b.playerId,
- conn: conn,
- out: make(chan []byte, 32),
- }
- b.players = append(b.players, player)
- b.playersMu.Unlock()
- defer func() {
- // Remove player.
- b.playersMu.Lock()
- defer b.playersMu.Unlock()
- index := -1
- for i, p := range b.players {
- if p.Id == player.Id {
- index = i
- break
- }
- }
- b.players = append(b.players[:index], b.players[index+1:]...)
- }()
- // Replay history.
- history := append([]Event{}, b.history...)
- previous := time.Now()
- for _, event := range history {
- dur := event.Time.Sub(previous)
- if dur.Seconds() > 1 {
- dur = time.Second
- }
- time.Sleep(dur)
- previous = event.Time
- sz := make([]byte, 2)
- binary.LittleEndian.PutUint16(sz, uint16(len(event.Msg)))
- _, err := conn.Write(sz)
- if err != nil {
- return err
- }
- _, err = conn.Write(event.Msg)
- if err != nil {
- return err
- }
- }
- go func() {
- for data := range player.out {
- _, err := player.conn.Write(data)
- if err != nil {
- log.Printf("p.conn.Write: %v", err)
- }
- }
- }()
- // Send WelcomeMessage.
- color := colors[int(player.Id)%len(colors)]
- _, err := conn.Write([]byte{6, 0, 2, b.playerId, color.R, color.G, color.B, color.A})
- if err != nil {
- return err
- }
- buf := make([]byte, 128*1024)
- for {
- // Receive message from player.
- _, err := io.ReadFull(conn, buf[:2])
- if err != nil {
- return err
- }
- size := binary.LittleEndian.Uint16(buf[:2])
- log.Printf("reading %d bytes", size)
- n, err := io.ReadFull(conn, buf[:size])
- if err != nil {
- return err
- }
- if n != int(size) {
- log.Printf("expected to get %d bytes, but got %d", size, n)
- continue
- }
- log.Printf("got message: %v", buf[:size])
- // Broadcast the message to all players except the one who sent it.
- msg := make([]byte, size)
- copy(msg, buf[:size])
- b.Broadcast(msg, player)
- // Save message to history.
- b.historyMu.Lock()
- if msg[0] == 3 {
- // Reset history on clear.
- b.history = nil
- } else {
- b.history = append(b.history, Event{
- Time: time.Now(),
- Msg: msg,
- })
- }
- b.historyMu.Unlock()
- }
- }
- func (b *Board) Broadcast(msg []byte, except *Player) {
- b.playersMu.Lock()
- defer b.playersMu.Unlock()
- data := make([]byte, 2+len(msg))
- binary.LittleEndian.PutUint16(data, uint16(len(msg)))
- copy(data[2:], msg)
- for _, p := range b.players {
- if p.Id != except.Id {
- log.Printf("sending msg to from %d to %d", except.Id, p.Id)
- p.out <- data
- }
- }
- // Calculate FPS.
- fps := 0
- min := time.Now().Add(-time.Second)
- for _, e := range b.history {
- if e.Time.After(min) {
- fps++
- }
- }
- log.Printf("fps is %d", fps)
- }
- func main() {
- b := NewBoard()
- l, err := net.Listen("tcp", ":3939")
- if err != nil {
- log.Fatal(err)
- }
- for {
- conn, err := l.Accept()
- if err != nil {
- log.Printf("Accept: %v", err)
- continue
- }
- go func() {
- if err := b.HandleConn(conn); err != nil {
- log.Printf("HandleConn: %v", err)
- }
- }()
- }
- }
Add Comment
Please, Sign In to add comment