multiserver/redirect.go

193 lines
3.9 KiB
Go

package main
import (
"encoding/binary"
"errors"
"log"
"net"
"github.com/anon55555/mt/rudp"
)
var ErrServerDoesNotExist = errors.New("server doesn't exist")
var ErrAlreadyConnected = errors.New("already connected to server")
var onRedirectDone []func(*Peer, string, bool)
// RegisterOnRedirectDone registers a callback function that is called
// when the Peer.Redirect method exits
func RegisterOnRedirectDone(function func(*Peer, string, bool)) {
onRedirectDone = append(onRedirectDone, function)
}
func processRedirectDone(p *Peer, newsrv string) {
success := p.ServerName() == newsrv
successstr := "false"
if success {
successstr = "true"
}
rpcSrvMu.Lock()
for srv := range rpcSrvs {
srv.doRpc("->REDIRECTED "+p.Username()+" "+newsrv+" "+successstr, "--")
}
rpcSrvMu.Unlock()
for i := range onRedirectDone {
onRedirectDone[i](p, newsrv, success)
}
}
// Redirect sends the Peer to the minetest server named newsrv
func (p *Peer) Redirect(newsrv string) error {
p.redirectMu.Lock()
defer p.redirectMu.Unlock()
defer processRedirectDone(p, newsrv)
straddr, ok := GetConfKey("servers:" + newsrv + ":address").(string)
if !ok {
return ErrServerDoesNotExist
}
if p.Server().Addr().String() == straddr {
return ErrAlreadyConnected
}
srvaddr, err := net.ResolveUDPAddr("udp", straddr)
if err != nil {
return err
}
conn, err := net.DialUDP("udp", nil, srvaddr)
if err != nil {
return err
}
srv, err := Connect(conn, conn.RemoteAddr())
if err != nil {
return err
}
// Reset formspec style
data := []byte{
0x00, ToClientFormspecPrepend,
0x00, 0x00,
}
_, err = p.Send(rudp.Pkt{Data: data})
// Remove active objects
data = make([]byte, 6+len(p.aoIDs)*2)
data[0] = uint8(0x00)
data[1] = uint8(ToClientActiveObjectRemoveAdd)
binary.BigEndian.PutUint16(data[2:4], uint16(len(p.aoIDs)))
si := 4
for ao := range p.aoIDs {
binary.BigEndian.PutUint16(data[si:2+si], ao)
si += 2
}
binary.BigEndian.PutUint16(data[si:2+si], uint16(0))
_, err = p.Send(rudp.Pkt{Data: data})
if err != nil {
return err
}
p.aoIDs = make(map[uint16]bool)
// Remove HUDs
data = []byte{0, ToClientHudSetParam, 0, 1, 0, 4, 0, 0, 0, 8}
_, err = p.Send(rudp.Pkt{ChNo: 1, Data: data})
if err != nil {
return err
}
data = []byte{0, ToClientHudSetParam, 0, 2, 0, 0}
_, err = p.Send(rudp.Pkt{ChNo: 1, Data: data})
if err != nil {
return err
}
data = []byte{0, ToClientHudSetParam, 0, 3, 0, 0}
_, err = p.Send(rudp.Pkt{ChNo: 1, Data: data})
if err != nil {
return err
}
for hud := range p.huds {
data = make([]byte, 6)
data[0] = uint8(0x00)
data[1] = uint8(ToClientHudRm)
binary.BigEndian.PutUint32(data[2:6], hud)
_, err = p.Send(rudp.Pkt{Data: data, ChNo: 1})
if err != nil {
return err
}
}
p.huds = make(map[uint32]bool)
// Stop looped sounds
for sound := range p.sounds {
data = make([]byte, 6)
data[0] = uint8(0x00)
data[1] = uint8(ToClientStopSound)
binary.BigEndian.PutUint32(data[2:6], uint32(sound))
_, err = p.Send(rudp.Pkt{Data: data})
}
p.sounds = make(map[int32]bool)
// Update detached inventories
if len(detachedinvs[newsrv]) > 0 {
for i := range detachedinvs[newsrv] {
data = make([]byte, 2+len(detachedinvs[newsrv][i]))
data[0] = uint8(0x00)
data[1] = uint8(ToClientDetachedInventory)
copy(data[2:], detachedinvs[newsrv][i])
_, err = p.Send(rudp.Pkt{Data: data})
if err != nil {
return err
}
}
}
p.Server().stopForwarding()
fin := make(chan *Peer) // close-only
go Init(p, srv, true, false, fin)
<-fin
p.SetServer(srv)
go Proxy(p, srv)
go Proxy(srv, p)
// Rejoin mod channels
for ch := range p.modChs {
data := make([]byte, 4+len(ch))
data[0] = uint8(0x00)
data[1] = uint8(ToServerModChannelJoin)
binary.BigEndian.PutUint16(data[2:4], uint16(len(ch)))
copy(data[4:], []byte(ch))
ack, err := srv.Send(rudp.Pkt{Data: data})
if err != nil {
log.Print(err)
}
<-ack
}
log.Print(p.Addr().String() + " redirected to " + newsrv)
return nil
}