mt-multiserver-proxy/server_conn.go

144 lines
2.6 KiB
Go

package proxy
import (
"errors"
"log"
"net"
"sync"
"time"
"github.com/anon55555/mt"
"github.com/anon55555/mt/rudp"
)
// A ServerConn is a connection to a minetest server.
type ServerConn struct {
mt.Peer
clt *ClientConn
mu sync.RWMutex
logger *log.Logger
cstate clientState
cstateMu sync.RWMutex
name string
initCh chan struct{}
auth struct {
method mt.AuthMethods
salt, srpA, a, srpK []byte
}
mediaPool string
inv mt.Inv
detachedInvs []string
aos map[mt.AOID]struct{}
particleSpawners map[mt.ParticleSpawnerID]struct{}
sounds map[mt.SoundID]struct{}
huds map[mt.HUDID]mt.HUDType
playerList map[string]struct{}
}
func (sc *ServerConn) client() *ClientConn {
sc.mu.RLock()
defer sc.mu.RUnlock()
return sc.clt
}
func (sc *ServerConn) state() clientState {
sc.cstateMu.RLock()
defer sc.cstateMu.RUnlock()
return sc.cstate
}
func (sc *ServerConn) setState(state clientState) {
sc.cstateMu.Lock()
defer sc.cstateMu.Unlock()
sc.cstate = state
}
// Init returns a channel that is closed
// when the ServerConn enters the csActive state.
func (sc *ServerConn) Init() <-chan struct{} { return sc.initCh }
// Log logs an interaction with the ServerConn.
// dir indicates the direction of the interaction.
func (sc *ServerConn) Log(dir string, v ...interface{}) {
sc.logger.Println(append([]interface{}{dir}, v...)...)
}
func handleSrv(sc *ServerConn) {
go func() {
init := make(chan struct{})
defer close(init)
go func(init <-chan struct{}) {
select {
case <-init:
case <-time.After(10 * time.Second):
sc.Log("->", "timeout")
sc.Close()
}
}(init)
for sc.state() == csCreated && sc.client() != nil {
sc.SendCmd(&mt.ToSrvInit{
SerializeVer: serializeVer,
MinProtoVer: protoVer,
MaxProtoVer: protoVer,
PlayerName: sc.client().Name(),
})
time.Sleep(500 * time.Millisecond)
}
}()
for {
pkt, err := sc.Recv()
if err != nil {
if errors.Is(err, net.ErrClosed) {
if errors.Is(sc.WhyClosed(), rudp.ErrTimedOut) {
sc.Log("<->", "timeout")
} else {
sc.Log("<->", "disconnect")
}
if sc.client() != nil {
ack, _ := sc.client().SendCmd(&mt.ToCltKick{
Reason: mt.Custom,
Custom: "Server connection closed unexpectedly.",
})
select {
case <-sc.client().Closed():
case <-ack:
sc.client().Close()
sc.client().mu.Lock()
sc.client().srv = nil
sc.client().mu.Unlock()
sc.mu.Lock()
sc.clt = nil
sc.mu.Unlock()
}
}
break
}
sc.Log("<-", err)
continue
}
sc.process(pkt)
}
}