multiserver/listen.go

222 lines
4.1 KiB
Go
Raw Normal View History

2021-01-05 11:34:35 -08:00
package multiserver
import (
"encoding/binary"
2021-01-09 03:26:30 -08:00
"errors"
2021-01-05 11:34:35 -08:00
"fmt"
2021-01-09 03:26:30 -08:00
"net"
"sync"
2021-01-05 11:34:35 -08:00
)
2021-01-06 05:42:55 -08:00
var ErrPlayerLimitReached = errors.New("player limit reached")
2021-01-05 11:34:35 -08:00
type Listener struct {
conn net.PacketConn
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
clts chan cltPeer
errs chan error
2021-01-09 03:26:30 -08:00
mu sync.Mutex
2021-01-05 11:34:35 -08:00
addr2peer map[string]cltPeer
2021-01-09 03:26:30 -08:00
id2peer map[PeerID]cltPeer
peerid PeerID
2021-01-05 11:34:35 -08:00
}
2021-01-06 14:39:54 -08:00
var listener *Listener
2021-01-05 11:34:35 -08:00
// Listen listens for packets on conn until it is closed
func Listen(conn net.PacketConn) *Listener {
l := &Listener{
conn: conn,
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
clts: make(chan cltPeer),
errs: make(chan error),
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
addr2peer: make(map[string]cltPeer),
id2peer: make(map[PeerID]cltPeer),
}
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
pkts := make(chan netPkt)
go readNetPkts(l.conn, pkts, l.errs)
go func() {
for pkt := range pkts {
if err := l.processNetPkt(pkt); err != nil {
l.errs <- err
}
}
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
close(l.clts)
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
for _, clt := range l.addr2peer {
clt.Close()
}
}()
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
return l
}
// Accept waits for and returns a connecting Peer
// You should keep calling this until it returns ErrClosed
// so it doesn't leak a goroutine
func (l *Listener) Accept() (*Peer, error) {
select {
case clt, ok := <-l.clts:
if !ok {
select {
case err := <-l.errs:
return nil, err
default:
return nil, ErrClosed
}
}
close(clt.accepted)
2021-01-09 03:26:30 -08:00
2021-01-06 05:42:55 -08:00
connectedPeers++
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
return clt.Peer, nil
case err := <-l.errs:
return nil, err
}
}
// Addr returns the net.PacketConn the Listener is listening on
func (l *Listener) Conn() net.PacketConn { return l.conn }
2021-01-17 12:43:23 -08:00
var ErrOutOfPeerIDs = errors.New("out of peer IDs")
2021-01-05 11:34:35 -08:00
type cltPeer struct {
*Peer
2021-01-09 03:26:30 -08:00
pkts chan<- netPkt
2021-01-05 11:34:35 -08:00
accepted chan struct{} // close-only
}
func (l *Listener) processNetPkt(pkt netPkt) error {
l.mu.Lock()
defer l.mu.Unlock()
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
addrstr := pkt.SrcAddr.String()
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
clt, ok := l.addr2peer[addrstr]
if !ok {
prev := l.peerid
for l.id2peer[l.peerid].Peer != nil || l.peerid < PeerIDCltMin {
2021-01-09 03:26:30 -08:00
if l.peerid == prev-1 {
2021-01-05 11:34:35 -08:00
return ErrOutOfPeerIDs
}
l.peerid++
}
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
pkts := make(chan netPkt, 256)
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
clt = cltPeer{
2021-01-09 03:26:30 -08:00
Peer: newPeer(l.conn, pkt.SrcAddr, l.peerid, PeerIDSrv),
pkts: pkts,
2021-01-05 11:34:35 -08:00
accepted: make(chan struct{}),
}
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
l.addr2peer[addrstr] = clt
l.id2peer[clt.ID()] = clt
2021-01-09 03:26:30 -08:00
data := make([]byte, 2+2)
2021-01-05 11:34:35 -08:00
data[0] = uint8(rawTypeCtl)
data[1] = uint8(ctlSetPeerID)
binary.BigEndian.PutUint16(data[2:4], uint16(clt.ID()))
if _, err := clt.sendRaw(rawPkt{Data: data}); err != nil {
return fmt.Errorf("can't set client peer id: %w", err)
}
2021-01-09 03:26:30 -08:00
2021-01-06 05:42:55 -08:00
var maxPeers int
maxPeersKey := GetConfKey("player_limit")
if maxPeersKey == nil || fmt.Sprintf("%T", maxPeersKey) != "int" {
maxPeers = -1
}
maxPeers = maxPeersKey.(int)
2021-01-09 03:26:30 -08:00
2021-01-06 05:42:55 -08:00
if GetPeerCount() >= maxPeers && maxPeers > -1 {
data := []byte{
2021-01-14 10:06:40 -08:00
uint8(0x00), uint8(ToClientAccessDenied),
uint8(AccessDeniedTooManyUsers), uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
2021-01-06 05:42:55 -08:00
}
2021-01-09 03:26:30 -08:00
2021-01-11 12:45:46 -08:00
_, err := clt.Send(Pkt{Data: data})
2021-01-06 05:42:55 -08:00
if err != nil {
return err
}
2021-01-09 03:26:30 -08:00
2021-01-06 05:42:55 -08:00
clt.SendDisco(0, true)
clt.Close()
2021-01-09 03:26:30 -08:00
2021-01-06 05:42:55 -08:00
return ErrPlayerLimitReached
}
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
go func() {
select {
case l.clts <- clt:
case <-clt.Disco():
}
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
clt.processNetPkts(pkts)
}()
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
go func() {
<-clt.Disco()
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
l.mu.Lock()
close(pkts)
delete(l.addr2peer, addrstr)
delete(l.id2peer, clt.ID())
l.mu.Unlock()
}()
}
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
select {
case <-clt.accepted:
clt.pkts <- pkt
default:
select {
case clt.pkts <- pkt:
default:
return fmt.Errorf("ignoring net pkt from %s because buf is full", addrstr)
}
}
2021-01-09 03:26:30 -08:00
2021-01-05 11:34:35 -08:00
return nil
}
2021-01-06 14:39:54 -08:00
2021-01-17 12:43:23 -08:00
// GetPeerByUsername returns the Peer that is using name for
// authentication
func (l *Listener) GetPeerByUsername(name string) *Peer {
peers := l.GetPeers()
for i := range peers {
if string(peers[i].username) == name {
return peers[i]
2021-01-14 10:06:40 -08:00
}
}
return nil
}
2021-01-17 12:43:23 -08:00
// GetPeers returns an array containing all connected client Peers
2021-01-14 10:06:40 -08:00
func (l *Listener) GetPeers() []*Peer {
l.mu.Lock()
defer l.mu.Unlock()
var r []*Peer
for i := range l.addr2peer {
r = append(r, l.addr2peer[i].Peer)
}
return r
2021-01-06 14:39:54 -08:00
}
2021-01-17 12:43:23 -08:00
// SetListener is used to make a listener available globally
// This can only be done once
2021-01-06 14:39:54 -08:00
func SetListener(l *Listener) {
2021-01-17 12:43:23 -08:00
if listener == nil {
listener = l
}
2021-01-06 14:39:54 -08:00
}
2021-01-17 12:43:23 -08:00
// GetListener returns the global listener
2021-01-06 14:39:54 -08:00
func GetListener() *Listener {
return listener
}