multiserver/conn.go

204 lines
4.0 KiB
Go

package main
import (
"bytes"
"fmt"
"net"
"sync"
"time"
"github.com/anon55555/mt"
"github.com/anon55555/mt/rudp"
)
var connectedConns int = 0
var connectedConnsMu sync.RWMutex
// A Conn is a connection to a client or server
type Conn struct {
*rudp.Conn
protoVer uint16
username string
srp_s []byte
srp_A []byte
srp_a []byte
srp_B []byte
srp_K []byte
authMech int
sudoMode bool
stopforward bool
forwardMu sync.RWMutex
redirectMu sync.Mutex
srvMu sync.RWMutex
srv *Conn
initAoReceived bool
aoIDs map[uint16]bool
localPlayerCao uint16
currentPlayerCao uint16
useRpcMu sync.RWMutex
useRpc bool
noClt bool
modChs map[string]bool
huds map[uint32]bool
sounds map[int32]bool
blocks [][3]int16
inv *mt.Inv
}
// ProtoVer returns the protocol version of the Conn
func (c *Conn) ProtoVer() uint16 { return c.protoVer }
// Addr returns the remote address of the Conn
func (c *Conn) Addr() net.Addr {
return c.Conn.RemoteAddr()
}
// Username returns the username of the Conn
// if it isn't a server
func (c *Conn) Username() string { return c.username }
// Forward reports whether the Proxy func should continue or stop
func (c *Conn) Forward() bool {
c.forwardMu.RLock()
defer c.forwardMu.RUnlock()
return !c.stopforward
}
// stopForwarding tells the Proxy func to stop
func (c *Conn) stopForwarding() {
c.forwardMu.Lock()
defer c.forwardMu.Unlock()
c.stopforward = true
}
// Server returns the Conn this Conn is connected to
// if it isn't a server
func (c *Conn) Server() *Conn {
c.srvMu.RLock()
defer c.srvMu.RUnlock()
return c.srv
}
// ServerName returns the name of the Conn this Conn is connected to
// if this Conn is not a server
func (c *Conn) ServerName() string {
servers := ConfKey("servers").(map[interface{}]interface{})
for server := range servers {
if ConfKey("servers:"+server.(string)+":address") == c.Server().Addr().String() {
return server.(string)
}
}
return ""
}
// SetServer sets the Conn this Conn is connected to
// if this Conn is not a server
func (c *Conn) SetServer(s *Conn) {
c.srvMu.Lock()
defer c.srvMu.Unlock()
c.srv = s
}
// UseRpc reports whether RPC messages can be sent to the Conn
func (c *Conn) UseRpc() bool {
c.useRpcMu.RLock()
defer c.useRpcMu.RUnlock()
return c.useRpc
}
// SetUseRpc sets the value returned by UseRpc
func (c *Conn) SetUseRpc(useRpc bool) {
c.useRpcMu.Lock()
defer c.useRpcMu.Unlock()
c.useRpc = useRpc
}
// NoClt reports whether the Conn is RPC-only
func (c *Conn) NoClt() bool { return c.noClt }
// MakeRpcOnly marks the Conn as RPC-only
func (c *Conn) MakeRpcOnly() {
c.noClt = true
}
// Inv returns the inventory of the Conn
func (c *Conn) Inv() *mt.Inv { return c.inv }
// CloseWith denies access and disconnects the Conn
func (c *Conn) CloseWith(reason uint8, custom string, reconnect bool) error {
defer c.Close()
w := bytes.NewBuffer([]byte{0x00, ToClientAccessDenied})
WriteUint8(w, reason)
WriteBytes16(w, []byte(custom))
if reconnect {
WriteUint8(w, 1)
} else {
WriteUint8(w, 0)
}
_, err := c.Send(rudp.Pkt{Reader: w})
if err != nil {
return err
}
return nil
}
// Connect connects to the server on conn
// and closes conn when the Conn disconnects
func Connect(conn net.Conn) (*Conn, error) {
srv := &Conn{Conn: rudp.Connect(conn)}
ack, err := srv.Send(rudp.Pkt{Reader: bytes.NewReader([]byte{0, 0})})
if err != nil {
return nil, err
}
select {
case <-time.After(8 * time.Second):
srv.Close()
return nil, fmt.Errorf("server at %s is unreachable", conn.RemoteAddr().String())
case <-ack:
}
return srv, nil
}
// ConnCount reports how many client Conns are connected
func ConnCount() int {
connectedConnsMu.RLock()
defer connectedConnsMu.RUnlock()
return connectedConns
}
// ConnsServer returns the client Conns that are connected to a server
func ConnsServer(server string) []*Conn {
var r []*Conn
for _, c := range Conns() {
if c.ServerName() == server {
r = append(r, c)
}
}
return r
}