You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

204 lines
4.1 KiB

package main
import (
var connectedConns int = 0
var connectedConnsMu sync.RWMutex
// A Conn is a connection to a client or server
type Conn struct {
protoVer uint16
formspecVer 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 }
// FormspecVer returns the formspec API version of the Conn
func (c *Conn) FormspecVer() uint16 { return c.formspecVer + 1 }
// 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 {
defer c.forwardMu.RUnlock()
return !c.stopforward
// stopForwarding tells the Proxy func to stop
func (c *Conn) stopForwarding() {
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 {
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) {
defer c.srvMu.Unlock()
c.srv = s
// UseRPC reports whether RPC messages can be sent to the Conn
func (c *Conn) UseRPC() bool {
defer c.useRPCMu.RUnlock()
return c.useRPC
// SetUseRPC sets the value returned by UseRPC
func (c *Conn) SetUseRPC(useRPC bool) {
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):
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 {
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