multiserver/redirect.go

400 lines
8.2 KiB
Go

package main
import (
"bytes"
"compress/zlib"
"encoding/binary"
"fmt"
"log"
"math"
"net"
"github.com/anon55555/mt/rudp"
)
var onRedirectDone []func(*Conn, string, bool)
// RegisterOnRedirectDone registers a callback function that is called
// when the Conn.Redirect method exits
func RegisterOnRedirectDone(function func(*Conn, string, bool)) {
onRedirectDone = append(onRedirectDone, function)
}
func processRedirectDone(c *Conn, newsrv *string) {
success := c.ServerName() == *newsrv
successstr := "false"
if success {
successstr = "true"
}
rpcSrvMu.Lock()
for srv := range rpcSrvs {
srv.doRpc("->REDIRECTED "+c.Username()+" "+*newsrv+" "+successstr, "--")
}
rpcSrvMu.Unlock()
for i := range onRedirectDone {
onRedirectDone[i](c, *newsrv, success)
}
}
// Redirect sends the Conn to the minetest server named newsrv
func (c *Conn) Redirect(newsrv string) error {
c.redirectMu.Lock()
defer c.redirectMu.Unlock()
defer processRedirectDone(c, &newsrv)
straddr, ok := ConfKey("servers:" + newsrv + ":address").(string)
if !ok {
grp, ok := ConfKey("groups:" + newsrv).([]interface{})
if !ok {
return fmt.Errorf("server or group %s does not exist", newsrv)
}
smallestCnt := int(^uint(0) >> 1)
for _, srv := range grp {
cnt := len(ConnsServer(srv.(string)))
if cnt < smallestCnt {
if c.ServerName() == srv.(string) {
return fmt.Errorf("already connected to server %s", srv.(string))
}
smallestCnt = cnt
newsrv = srv.(string)
}
}
straddr, ok = ConfKey("servers:" + newsrv + ":address").(string)
if !ok {
return fmt.Errorf("server %s does not exist", newsrv)
}
}
if c.ServerName() == newsrv {
return fmt.Errorf("already connected to server %s", newsrv)
}
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)
if err != nil {
return err
}
fin := make(chan *Conn)
go Init(c, srv, true, false, fin)
initOk := <-fin
if initOk == nil {
srv.Close()
return fmt.Errorf("initialization with server %s failed", newsrv)
}
// Reset formspec style
data := []byte{
0x00, ToClientFormspecPrepend,
0x00, 0x00,
}
_, err = c.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
// Remove active objects
data = make([]byte, 6+len(c.aoIDs)*2)
data[0] = uint8(0x00)
data[1] = uint8(ToClientActiveObjectRemoveAdd)
binary.BigEndian.PutUint16(data[2:4], uint16(len(c.aoIDs)))
si := 4
for ao := range c.aoIDs {
binary.BigEndian.PutUint16(data[si:2+si], ao)
si += 2
}
binary.BigEndian.PutUint16(data[si:2+si], uint16(0))
_, err = c.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
if err != nil {
return err
}
c.aoIDs = make(map[uint16]bool)
// Remove MapBlocks
for _, block := range c.blocks {
x := block[0]
y := block[1]
z := block[2]
blockdata := make([]byte, 5)
blockdata[0] = uint8(0)
binary.BigEndian.PutUint16(blockdata[1:3], uint16(0xFFFF))
blockdata[3] = uint8(2)
blockdata[4] = uint8(2)
nodes := make([]byte, 16384)
for i := uint32(0); i < NodeCount; i++ {
binary.BigEndian.PutUint16(nodes[2*i:2+2*i], uint16(ContentIgnore))
nodes[2*NodeCount+i] = uint8(0)
nodes[3*NodeCount+i] = uint8(0)
}
var compBuf bytes.Buffer
zw := zlib.NewWriter(&compBuf)
zw.Write(nodes)
zw.Close()
compNodes := compBuf.Bytes()
w := bytes.NewBuffer([]byte{0x00, ToClientBlockdata})
WriteUint16(w, uint16(x))
WriteUint16(w, uint16(y))
WriteUint16(w, uint16(z))
w.Write(blockdata)
w.Write(compNodes)
_, err = c.Send(rudp.Pkt{Reader: w})
if err != nil {
return err
}
}
c.blocks = [][3]int16{}
// Remove HUDs
data = []byte{0, ToClientHudSetParam, 0, 1, 0, 4, 0, 0, 0, 8}
_, err = c.Send(rudp.Pkt{
Reader: bytes.NewReader(data),
PktInfo: rudp.PktInfo{
Channel: 1,
},
})
if err != nil {
return err
}
data = []byte{0, ToClientHudSetParam, 0, 2, 0, 0}
_, err = c.Send(rudp.Pkt{
Reader: bytes.NewReader(data),
PktInfo: rudp.PktInfo{
Channel: 1,
},
})
if err != nil {
return err
}
data = []byte{0, ToClientHudSetParam, 0, 3, 0, 0}
_, err = c.Send(rudp.Pkt{
Reader: bytes.NewReader(data),
PktInfo: rudp.PktInfo{
Channel: 1,
},
})
if err != nil {
return err
}
for hud := range c.huds {
data = make([]byte, 6)
data[0] = uint8(0x00)
data[1] = uint8(ToClientHudRM)
binary.BigEndian.PutUint32(data[2:6], hud)
_, err = c.Send(rudp.Pkt{
Reader: bytes.NewReader(data),
PktInfo: rudp.PktInfo{
Channel: 1,
},
})
if err != nil {
return err
}
}
c.huds = make(map[uint32]bool)
// Stop looped sounds
for sound := range c.sounds {
data = make([]byte, 6)
data[0] = uint8(0x00)
data[1] = uint8(ToClientStopSound)
binary.BigEndian.PutUint32(data[2:6], uint32(sound))
_, err = c.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
if err != nil {
return err
}
}
c.sounds = make(map[int32]bool)
// Stop day/night ratio override
data = []byte{0, 0, 0}
_, err = c.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
if err != nil {
return err
}
// Reset eye offset
data = []byte{}
for i := 0; i < 24; i++ {
data = append(data, uint8(0))
}
_, err = c.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
if err != nil {
return err
}
// Reset sky
switch c.ProtoVer() {
case 39:
data = []byte{
0, ToClientSetSky,
0, 0, 0, 0,
0, 7, 114, 101, 103, 117, 108, 97, 114,
1,
255, 255, 255, 255,
255, 255, 255, 255,
0, 7, 100, 101, 102, 97, 117, 108, 116,
255, 97, 181, 245,
255, 144, 211, 245,
255, 180, 186, 250,
255, 186, 193, 240,
255, 0, 107, 255,
255, 64, 144, 255,
255, 100, 100, 100,
}
default:
data = []byte{
0, ToClientSetSky,
0, 0, 0, 0,
0, 7, 114, 101, 103, 117, 108, 97, 114,
0, 0,
}
}
_, err = c.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
if err != nil {
return err
}
// Reset sun
data = []byte{
1,
0, 7, 115, 117, 110, 46, 112, 110, 103,
0, 15, 115, 117, 110, 95, 116, 111, 110, 101, 109, 97, 112, 46, 112, 110, 103,
0, 13, 115, 117, 110, 114, 105, 115, 101, 98, 103, 46, 112, 110, 103,
}
sunscale := make([]byte, 4)
binary.BigEndian.PutUint32(sunscale[0:4], math.Float32bits(1))
data = append(data, sunscale...)
_, err = c.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
if err != nil {
return err
}
// Reset moon
data = []byte{
1,
0, 8, 109, 111, 111, 110, 46, 112, 110, 103,
0, 16, 109, 111, 111, 110, 95, 116, 111, 110, 101, 109, 97, 112, 46, 112, 110, 103,
}
moonscale := make([]byte, 4)
binary.BigEndian.PutUint32(moonscale, math.Float32bits(1))
data = append(data, moonscale...)
_, err = c.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
if err != nil {
return err
}
// Reset stars
data = []byte{
1,
0, 0, 3, 232,
105, 235, 235, 255,
}
starscale := make([]byte, 4)
binary.BigEndian.PutUint32(starscale, math.Float32bits(1))
data = append(data, starscale...)
_, err = c.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
if err != nil {
return err
}
// Reset cloud params
w := bytes.NewBuffer([]byte{0x00, ToClientCloudParams})
WriteUint32(w, math.Float32bits(0))
w.Write([]byte{0, 0, 0, 0, 0, 0, 0, 0})
WriteUint32(w, math.Float32bits(0))
WriteUint32(w, math.Float32bits(0))
WriteUint32(w, math.Float32bits(0))
WriteUint32(w, math.Float32bits(0))
_, err = c.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
if err != nil {
return err
}
// 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 = c.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
if err != nil {
return err
}
}
}
c.Server().stopForwarding()
c.SetServer(srv)
go Proxy(c, srv)
go Proxy(srv, c)
// Rejoin mod channels
for ch := range c.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))
_, err = srv.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
if err != nil {
log.Print(err)
}
}
log.Print(c.Addr().String() + " redirected to " + newsrv)
return nil
}