Compare commits

...

5 Commits

Author SHA1 Message Date
HimbeerserverDE b4f690ffd8
Deprecate this version 2022-01-21 15:37:20 +01:00
HimbeerserverDE 2e9d4af86e
Better terminal input handling + Cursor movement (#80) 2021-05-02 17:57:17 +02:00
HimbeerserverDE 132293e6b1
Autocomplete parts of commands and names 2021-05-02 14:19:22 +02:00
HimbeerserverDE 69e10e956b
Fix RPC func casing 2021-05-02 13:12:24 +02:00
HimbeerserverDE 178c47e02f
Fix player name regexp 2021-05-02 13:02:50 +02:00
8 changed files with 105 additions and 68 deletions

View File

@ -1,6 +1,11 @@
# multiserver # multiserver
Minetest reverse proxy supporting multiple servers and media multiplexing Minetest reverse proxy supporting multiple servers and media multiplexing
## Obsolete
This version has many bugs, is __NOT__ updated to work with Minetest 5.4.1
and is unlikely to receive any future updates.
It's untested and the code isn't very clean. __You should use [mt-multiserver-proxy](https://github.com/HimbeerserverDE/mt-multiserver-proxy) instead.__
## Credits ## Credits
This project was made possible by [anon55555's Minetest RUDP package](https://github.com/anon55555/mt/tree/master/rudp). This project was made possible by [anon55555's Minetest RUDP package](https://github.com/anon55555/mt/tree/master/rudp).

View File

@ -157,10 +157,10 @@ func processPktCommand(src, dst *Conn, pkt *rudp.Pkt) bool {
if ch == rpcCh { if ch == rpcCh {
switch sig := ReadUint8(r); sig { switch sig := ReadUint8(r); sig {
case ModChSigJoinOk: case ModChSigJoinOk:
src.SetUseRpc(true) src.SetUseRPC(true)
case ModChSigSetState: case ModChSigSetState:
if state == ModChStateRO { if state == ModChStateRO {
src.SetUseRpc(false) src.SetUseRPC(false)
} }
} }
return true return true
@ -168,7 +168,7 @@ func processPktCommand(src, dst *Conn, pkt *rudp.Pkt) bool {
return false return false
case ToClientModChannelMSG: case ToClientModChannelMSG:
return processRpc(src, r) return processRPC(src, r)
case ToClientBlockdata: case ToClientBlockdata:
data, drop := processBlockdata(dst, r) data, drop := processBlockdata(dst, r)
if drop { if drop {

36
conn.go
View File

@ -42,9 +42,9 @@ type Conn struct {
localPlayerCao uint16 localPlayerCao uint16
currentPlayerCao uint16 currentPlayerCao uint16
useRpcMu sync.RWMutex useRPCMu sync.RWMutex
useRpc bool useRPC bool
noClt bool noCLT bool
modChs map[string]bool modChs map[string]bool
huds map[uint32]bool huds map[uint32]bool
@ -115,28 +115,28 @@ func (c *Conn) SetServer(s *Conn) {
c.srv = s c.srv = s
} }
// UseRpc reports whether RPC messages can be sent to the Conn // UseRPC reports whether RPC messages can be sent to the Conn
func (c *Conn) UseRpc() bool { func (c *Conn) UseRPC() bool {
c.useRpcMu.RLock() c.useRPCMu.RLock()
defer c.useRpcMu.RUnlock() defer c.useRPCMu.RUnlock()
return c.useRpc return c.useRPC
} }
// SetUseRpc sets the value returned by UseRpc // SetUseRPC sets the value returned by UseRPC
func (c *Conn) SetUseRpc(useRpc bool) { func (c *Conn) SetUseRPC(useRPC bool) {
c.useRpcMu.Lock() c.useRPCMu.Lock()
defer c.useRpcMu.Unlock() defer c.useRPCMu.Unlock()
c.useRpc = useRpc c.useRPC = useRPC
} }
// NoClt reports whether the Conn is RPC-only // NoCLT reports whether the Conn is RPC-only
func (c *Conn) NoClt() bool { return c.noClt } func (c *Conn) NoCLT() bool { return c.noCLT }
// MakeRpcOnly marks the Conn as RPC-only // MakeRPCOnly marks the Conn as RPC-only
func (c *Conn) MakeRpcOnly() { func (c *Conn) MakeRPCOnly() {
c.noClt = true c.noCLT = true
} }
// Inv returns the inventory of the Conn // Inv returns the inventory of the Conn

View File

@ -10,6 +10,7 @@ import (
) )
var consoleInput []rune var consoleInput []rune
var cursorPos int
func draw(msgs []string) { func draw(msgs []string) {
prompt, ok := ConfKey("console_prompt").(string) prompt, ok := ConfKey("console_prompt").(string)
@ -33,6 +34,11 @@ func draw(msgs []string) {
} }
gocurses.Mvaddstr(row-i-1, 0, prompt+string(consoleInput)) gocurses.Mvaddstr(row-i-1, 0, prompt+string(consoleInput))
if cursorPos >= len(consoleInput) {
cursorPos = len(consoleInput)
}
gocurses.Mvaddstr(row-i-1, len(prompt)+len(string(consoleInput))-cursorPos, "")
gocurses.Refresh() gocurses.Refresh()
} }
@ -45,6 +51,8 @@ func autoComplete(all []string, current string) string {
} else { } else {
return all[0] return all[0]
} }
} else if strings.HasPrefix(v, current) {
return v
} }
} }
} else if len(all) >= 1 { } else if len(all) >= 1 {
@ -99,12 +107,16 @@ func initCurses(l *Logger) {
for { for {
var ch rune var ch rune
ch1 := gocurses.Stdscr.Getch() % 255 ch1 := gocurses.Getch()
if ch1 > 0x7F { if ch1%255 > 0x7F {
ch2 := gocurses.Stdscr.Getch() ch2 := gocurses.Getch()
ch, _ = utf8.DecodeRune([]byte{byte(ch1), byte(ch2)}) ch, _ = utf8.DecodeRune([]byte{byte(ch1), byte(ch2)})
} else { } else {
ch = rune(ch1) if ch1 != 3 && ch1 != 4 && ch1 != 5 && ch1 != 6 && ch1 != 339 && ch1 != 338 {
ch = rune(ch1 % 255)
} else {
ch = rune(ch1)
}
} }
switch ch { switch ch {
@ -113,6 +125,16 @@ func initCurses(l *Logger) {
case 4: case 4:
consoleInput = h.Prev(consoleInput) consoleInput = h.Prev(consoleInput)
case 5: case 5:
cursorPos += 1
if cursorPos > len(consoleInput) {
cursorPos = len(consoleInput)
}
case 6:
cursorPos -= 1
if cursorPos < 0 {
cursorPos = 0
}
case 339:
rows, _ := gocurses.Getmaxyx() rows, _ := gocurses.Getmaxyx()
start := len(l.lines) - rows + 1 - l.offset start := len(l.lines) - rows + 1 - l.offset
if start < 0 { if start < 0 {
@ -125,14 +147,18 @@ func initCurses(l *Logger) {
l.offset = len(l.lines) - 1 l.offset = len(l.lines) - 1
} }
} }
case 6: case 338:
l.offset -= 1 l.offset -= 1
if l.offset < 0 { if l.offset < 0 {
l.offset = 0 l.offset = 0
} }
case '\b': case '\b':
if len(consoleInput) > 0 { if len(consoleInput) > 0 {
consoleInput = consoleInput[:len(consoleInput)-1] if cursorPos > 0 {
consoleInput = append(consoleInput[:len(consoleInput)-cursorPos-1], consoleInput[len(consoleInput)-cursorPos:]...)
} else {
consoleInput = consoleInput[:len(consoleInput)-1]
}
} }
case '\t': case '\t':
if strings.Count(string(consoleInput), " ") > 0 { if strings.Count(string(consoleInput), " ") > 0 {
@ -157,7 +183,11 @@ func initCurses(l *Logger) {
chatCommands[params[0]].function(nil, strings.Join(params[1:], " ")) chatCommands[params[0]].function(nil, strings.Join(params[1:], " "))
default: default:
consoleInput = append(consoleInput, ch) if cursorPos > 0 {
consoleInput = append(consoleInput[:len(consoleInput)-cursorPos], append([]rune{ch}, consoleInput[len(consoleInput)-cursorPos:]...)...)
} else {
consoleInput = append(consoleInput, ch)
}
} }
rows, _ := gocurses.Getmaxyx() rows, _ := gocurses.Getmaxyx()

View File

@ -4,7 +4,7 @@ import "sync"
const ( const (
MaxPlayerNameLength = 20 MaxPlayerNameLength = 20
PlayerNameChars = "[a-zA-Z0-9-_]" PlayerNameChars = "^[a-zA-Z0-9-_]*$"
) )
var onlinePlayers map[string]bool var onlinePlayers map[string]bool
@ -35,7 +35,7 @@ func processJoin(c *Conn) {
rpcSrvMu.Lock() rpcSrvMu.Lock()
for srv := range rpcSrvs { for srv := range rpcSrvs {
srv.doRpc("->JOIN "+c.Username()+" "+cltSrv, "--") srv.doRPC("->JOIN "+c.Username()+" "+cltSrv, "--")
} }
rpcSrvMu.Unlock() rpcSrvMu.Unlock()
@ -53,7 +53,7 @@ func processLeave(c *Conn) {
rpcSrvMu.Lock() rpcSrvMu.Lock()
for srv := range rpcSrvs { for srv := range rpcSrvs {
srv.doRpc("->LEAVE "+c.Username(), "--") srv.doRPC("->LEAVE "+c.Username(), "--")
} }
rpcSrvMu.Unlock() rpcSrvMu.Unlock()

View File

@ -30,7 +30,7 @@ func processRedirectDone(c *Conn, newsrv *string) {
rpcSrvMu.Lock() rpcSrvMu.Lock()
for srv := range rpcSrvs { for srv := range rpcSrvs {
srv.doRpc("->REDIRECTED "+c.Username()+" "+*newsrv+" "+successstr, "--") srv.doRPC("->REDIRECTED "+c.Username()+" "+*newsrv+" "+successstr, "--")
} }
rpcSrvMu.Unlock() rpcSrvMu.Unlock()

70
rpc.go
View File

@ -35,7 +35,7 @@ const (
var rpcSrvMu sync.Mutex var rpcSrvMu sync.Mutex
var rpcSrvs map[*Conn]struct{} var rpcSrvs map[*Conn]struct{}
func (c *Conn) joinRpc() { func (c *Conn) joinRPC() {
data := make([]byte, 4+len(rpcCh)) data := make([]byte, 4+len(rpcCh))
data[0] = uint8(0x00) data[0] = uint8(0x00)
data[1] = uint8(ToServerModChannelJoin) data[1] = uint8(ToServerModChannelJoin)
@ -49,7 +49,7 @@ func (c *Conn) joinRpc() {
<-ack <-ack
} }
func (c *Conn) leaveRpc() { func (c *Conn) leaveRPC() {
data := make([]byte, 4+len(rpcCh)) data := make([]byte, 4+len(rpcCh))
data[0] = uint8(0x00) data[0] = uint8(0x00)
data[1] = uint8(ToServerModChannelLeave) data[1] = uint8(ToServerModChannelLeave)
@ -63,7 +63,7 @@ func (c *Conn) leaveRpc() {
<-ack <-ack
} }
func processRpc(c *Conn, r *bytes.Reader) bool { func processRPC(c *Conn, r *bytes.Reader) bool {
ch := string(ReadBytes16(r)) ch := string(ReadBytes16(r))
sender := string(ReadBytes16(r)) sender := string(ReadBytes16(r))
msg := string(ReadBytes16(r)) msg := string(ReadBytes16(r))
@ -84,16 +84,16 @@ func processRpc(c *Conn, r *bytes.Reader) bool {
if !ok { if !ok {
return true return true
} }
go c.doRpc("->DEFSRV "+defsrv, rq) go c.doRPC("->DEFSRV "+defsrv, rq)
case "<-GETPEERCNT": case "<-GETPEERCNT":
cnt := strconv.Itoa(ConnCount()) cnt := strconv.Itoa(ConnCount())
go c.doRpc("->PEERCNT "+cnt, rq) go c.doRPC("->PEERCNT "+cnt, rq)
case "<-ISONLINE": case "<-ISONLINE":
online := "false" online := "false"
if IsOnline(strings.Join(strings.Split(msg, " ")[2:], " ")) { if IsOnline(strings.Join(strings.Split(msg, " ")[2:], " ")) {
online = "true" online = "true"
} }
go c.doRpc("->ISONLINE "+online, rq) go c.doRPC("->ISONLINE "+online, rq)
case "<-CHECKPRIVS": case "<-CHECKPRIVS":
name := strings.Split(msg, " ")[2] name := strings.Split(msg, " ")[2]
privs := decodePrivs(strings.Join(strings.Split(msg, " ")[3:], " ")) privs := decodePrivs(strings.Join(strings.Split(msg, " ")[3:], " "))
@ -104,7 +104,7 @@ func processRpc(c *Conn, r *bytes.Reader) bool {
hasprivs = "true" hasprivs = "true"
} }
go c.doRpc("->HASPRIVS "+hasprivs, rq) go c.doRPC("->HASPRIVS "+hasprivs, rq)
case "<-GETPRIVS": case "<-GETPRIVS":
name := strings.Split(msg, " ")[2] name := strings.Split(msg, " ")[2]
var r string var r string
@ -114,7 +114,7 @@ func processRpc(c *Conn, r *bytes.Reader) bool {
r = strings.Replace(encodePrivs(privs), "|", ",", -1) r = strings.Replace(encodePrivs(privs), "|", ",", -1)
} }
go c.doRpc("->PRIVS "+r, rq) go c.doRPC("->PRIVS "+r, rq)
case "<-SETPRIVS": case "<-SETPRIVS":
name := strings.Split(msg, " ")[2] name := strings.Split(msg, " ")[2]
privs := decodePrivs(strings.Join(strings.Split(msg, " ")[3:], " ")) privs := decodePrivs(strings.Join(strings.Split(msg, " ")[3:], " "))
@ -126,7 +126,7 @@ func processRpc(c *Conn, r *bytes.Reader) bool {
if IsOnline(name) { if IsOnline(name) {
srv = ConnByUsername(name).ServerName() srv = ConnByUsername(name).ServerName()
} }
go c.doRpc("->SRV "+srv, rq) go c.doRPC("->SRV "+srv, rq)
case "<-REDIRECT": case "<-REDIRECT":
name := strings.Split(msg, " ")[2] name := strings.Split(msg, " ")[2]
tosrv := strings.Split(msg, " ")[3] tosrv := strings.Split(msg, " ")[3]
@ -139,7 +139,7 @@ func processRpc(c *Conn, r *bytes.Reader) bool {
if IsOnline(name) { if IsOnline(name) {
addr = ConnByUsername(name).Addr().String() addr = ConnByUsername(name).Addr().String()
} }
go c.doRpc("->ADDR "+addr, rq) go c.doRPC("->ADDR "+addr, rq)
case "<-ISBANNED": case "<-ISBANNED":
target := strings.Split(msg, " ")[2] target := strings.Split(msg, " ")[2]
@ -157,7 +157,7 @@ func processRpc(c *Conn, r *bytes.Reader) bool {
r = "true" r = "true"
} }
go c.doRpc("->ISBANNED "+r, rq) go c.doRPC("->ISBANNED "+r, rq)
case "<-BAN": case "<-BAN":
target := strings.Split(msg, " ")[2] target := strings.Split(msg, " ")[2]
err := Ban(target, "not known") err := Ban(target, "not known")
@ -181,13 +181,13 @@ func processRpc(c *Conn, r *bytes.Reader) bool {
} }
srvs = srvs[:len(srvs)-1] srvs = srvs[:len(srvs)-1]
go c.doRpc("->SRVS "+srvs, rq) go c.doRPC("->SRVS "+srvs, rq)
case "<-MT2MT": case "<-MT2MT":
msg := strings.Join(strings.Split(msg, " ")[2:], " ") msg := strings.Join(strings.Split(msg, " ")[2:], " ")
rpcSrvMu.Lock() rpcSrvMu.Lock()
for srv := range rpcSrvs { for srv := range rpcSrvs {
if srv.Addr().String() != c.Addr().String() { if srv.Addr().String() != c.Addr().String() {
go srv.doRpc("->MT2MT true "+msg, "--") go srv.doRPC("->MT2MT true "+msg, "--")
} }
} }
rpcSrvMu.Unlock() rpcSrvMu.Unlock()
@ -202,7 +202,7 @@ func processRpc(c *Conn, r *bytes.Reader) bool {
rpcSrvMu.Lock() rpcSrvMu.Lock()
for srv := range rpcSrvs { for srv := range rpcSrvs {
if srv.Addr().String() == addr { if srv.Addr().String() == addr {
go srv.doRpc("->MT2MT false "+msg, "--") go srv.doRPC("->MT2MT false "+msg, "--")
} }
} }
rpcSrvMu.Unlock() rpcSrvMu.Unlock()
@ -211,8 +211,8 @@ func processRpc(c *Conn, r *bytes.Reader) bool {
return true return true
} }
func (c *Conn) doRpc(rpc, rq string) { func (c *Conn) doRPC(rpc, rq string) {
if !c.UseRpc() { if !c.UseRPC() {
return return
} }
@ -230,7 +230,7 @@ func (c *Conn) doRpc(rpc, rq string) {
} }
} }
func connectRpc() { func connectRPC() {
log.Print("Establishing RPC connections") log.Print("Establishing RPC connections")
servers := ConfKey("servers").(map[interface{}]interface{}) servers := ConfKey("servers").(map[interface{}]interface{})
@ -267,14 +267,14 @@ func connectRpc() {
rpcSrvs[srv] = struct{}{} rpcSrvs[srv] = struct{}{}
rpcSrvMu.Unlock() rpcSrvMu.Unlock()
go srv.joinRpc() go srv.joinRPC()
go handleRpc(srv) go handleRPC(srv)
}() }()
} }
} }
func handleRpc(srv *Conn) { func handleRPC(srv *Conn) {
srv.MakeRpcOnly() srv.MakeRPCOnly()
for { for {
pkt, err := srv.Recv() pkt, err := srv.Recv()
if err != nil { if err != nil {
@ -303,15 +303,15 @@ func handleRpc(srv *Conn) {
switch sig := ReadUint8(r); sig { switch sig := ReadUint8(r); sig {
case ModChSigJoinOk: case ModChSigJoinOk:
srv.SetUseRpc(true) srv.SetUseRPC(true)
case ModChSigSetState: case ModChSigSetState:
if state == ModChStateRO { if state == ModChStateRO {
srv.SetUseRpc(false) srv.SetUseRPC(false)
} }
} }
} }
case ToClientModChannelMSG: case ToClientModChannelMSG:
processRpc(srv, r) processRPC(srv, r)
} }
} }
} }
@ -327,18 +327,18 @@ ServerLoop:
continue continue
} }
if c2.Server().Addr().String() == c.Addr().String() { if c2.Server().Addr().String() == c.Addr().String() {
if c.NoClt() { if c.NoCLT() {
c.Close() c.Close()
} else { } else {
c.SetUseRpc(false) c.SetUseRPC(false)
c.leaveRpc() c.leaveRPC()
} }
delete(rpcSrvs, c) delete(rpcSrvs, c)
c3 := c2.Server() c3 := c2.Server()
c3.SetUseRpc(true) c3.SetUseRPC(true)
c3.joinRpc() c3.joinRPC()
rpcSrvs[c3] = struct{}{} rpcSrvs[c3] = struct{}{}
@ -358,10 +358,10 @@ ServerLoop:
} }
} }
go reconnectRpc(false) go reconnectRPC(false)
} }
func reconnectRpc(media bool) { func reconnectRPC(media bool) {
servers := ConfKey("servers").(map[interface{}]interface{}) servers := ConfKey("servers").(map[interface{}]interface{})
ServerLoop: ServerLoop:
for server := range servers { for server := range servers {
@ -412,8 +412,8 @@ ServerLoop:
rpcSrvs[srv] = struct{}{} rpcSrvs[srv] = struct{}{}
rpcSrvMu.Unlock() rpcSrvMu.Unlock()
go srv.joinRpc() go srv.joinRPC()
go handleRpc(srv) go handleRPC(srv)
}() }()
} }
} }
@ -428,7 +428,7 @@ func init() {
reconnect = 600 reconnect = 600
} }
connectRpc() connectRPC()
go func() { go func() {
reconnect := time.NewTicker(time.Duration(reconnect) * time.Second) reconnect := time.NewTicker(time.Duration(reconnect) * time.Second)
@ -436,7 +436,7 @@ func init() {
select { select {
case <-reconnect.C: case <-reconnect.C:
log.Print("Reintegrating servers") log.Print("Reintegrating servers")
reconnectRpc(true) reconnectRPC(true)
} }
} }
}() }()

View File

@ -17,6 +17,8 @@ const (
AnnounceDelete = "delete" AnnounceDelete = "delete"
) )
const verString = "multiserver v1.13.3"
func Announce(action string) error { func Announce(action string) error {
listsrv, ok := ConfKey("serverlist_url").(string) listsrv, ok := ConfKey("serverlist_url").(string)
if !ok { if !ok {
@ -76,7 +78,7 @@ func Announce(action string) error {
if action != AnnounceDelete { if action != AnnounceDelete {
data["name"] = conf("serverlist_name") data["name"] = conf("serverlist_name")
data["description"] = conf("serverlist_desc") data["description"] = conf("serverlist_desc")
data["version"] = "multiserver v1.13.2" data["version"] = verString
data["proto_min"] = ProtoMin data["proto_min"] = ProtoMin
data["proto_max"] = ProtoLatest data["proto_max"] = ProtoLatest
data["url"] = conf("serverlist_display_url") data["url"] = conf("serverlist_display_url")