aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--activeobject.go6
-rw-r--r--auth.go21
-rw-r--r--auth_sqlite3.go22
-rw-r--r--client_conn.go136
-rw-r--r--cmd/mt-multiserver-proxy/main.go (renamed from main.go)51
-rw-r--r--config.go6
-rw-r--r--connect.go16
-rw-r--r--content.go16
-rw-r--r--default_textures.go2
-rw-r--r--formspec.go4
-rw-r--r--gen_textures.go2
-rw-r--r--hop.go12
-rw-r--r--listen.go41
-rw-r--r--log.go2
-rw-r--r--players.go2
-rw-r--r--plugin.go4
-rw-r--r--server_conn.go54
18 files changed, 211 insertions, 188 deletions
diff --git a/README.md b/README.md
index ed08e42..db61484 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ multiple Minetest servers together. It is the successor to multiserver.
## Installation
Go 1.17 or higher is required. Run
-`go install github.com/HimbeerserverDE/mt-multiserver-proxy`
+`go install github.com/HimbeerserverDE/mt-multiserver-proxy/cmd/mt-multiserver/proxy`
to download and compile the project. A mt-multiserver-proxy executable
will be created in your $GOBIN directory.
diff --git a/activeobject.go b/activeobject.go
index 7bba616..aacd95f 100644
--- a/activeobject.go
+++ b/activeobject.go
@@ -1,8 +1,8 @@
-package main
+package proxy
import "github.com/anon55555/mt"
-func (sc *serverConn) swapAOID(ao *mt.AOID) {
+func (sc *ServerConn) swapAOID(ao *mt.AOID) {
if sc.client() != nil {
if *ao == sc.client().playerCAO {
*ao = sc.client().currentCAO
@@ -12,7 +12,7 @@ func (sc *serverConn) swapAOID(ao *mt.AOID) {
}
}
-func (sc *serverConn) handleAOMsg(aoMsg mt.AOMsg) {
+func (sc *ServerConn) handleAOMsg(aoMsg mt.AOMsg) {
switch msg := aoMsg.(type) {
case *mt.AOCmdAttach:
sc.swapAOID(&msg.Attach.ParentID)
diff --git a/auth.go b/auth.go
index 2f7b0c4..3334ebf 100644
--- a/auth.go
+++ b/auth.go
@@ -1,8 +1,12 @@
-package main
+package proxy
-import "time"
+import (
+ "errors"
+ "time"
+)
-var authIface authBackend
+var authIface AuthBackend
+var ErrAuthBackendExists = errors.New("auth backend already set")
type user struct {
name string
@@ -11,7 +15,7 @@ type user struct {
timestamp time.Time
}
-type authBackend interface {
+type AuthBackend interface {
Exists(name string) bool
Passwd(name string) (salt, verifier []byte, err error)
SetPasswd(name string, salt, verifier []byte) error
@@ -19,3 +23,12 @@ type authBackend interface {
Import(data []user)
Export() ([]user, error)
}
+
+func SetAuthBackend(ab AuthBackend) error {
+ if authIface != nil {
+ return ErrAuthBackendExists
+ }
+
+ authIface = ab
+ return nil
+}
diff --git a/auth_sqlite3.go b/auth_sqlite3.go
index 84dcf83..8574768 100644
--- a/auth_sqlite3.go
+++ b/auth_sqlite3.go
@@ -1,4 +1,4 @@
-package main
+package proxy
import (
"database/sql"
@@ -9,11 +9,11 @@ import (
_ "github.com/mattn/go-sqlite3"
)
-type authSQLite3 struct {
+type AuthSQLite3 struct {
db *sql.DB
}
-func (a authSQLite3) Exists(name string) bool {
+func (a AuthSQLite3) Exists(name string) bool {
if err := a.init(); err != nil {
return false
}
@@ -24,7 +24,7 @@ func (a authSQLite3) Exists(name string) bool {
return err == nil
}
-func (a authSQLite3) Passwd(name string) (salt, verifier []byte, err error) {
+func (a AuthSQLite3) Passwd(name string) (salt, verifier []byte, err error) {
if err = a.init(); err != nil {
return
}
@@ -35,7 +35,7 @@ func (a authSQLite3) Passwd(name string) (salt, verifier []byte, err error) {
return
}
-func (a authSQLite3) SetPasswd(name string, salt, verifier []byte) error {
+func (a AuthSQLite3) SetPasswd(name string, salt, verifier []byte) error {
if err := a.init(); err != nil {
return err
}
@@ -50,7 +50,7 @@ func (a authSQLite3) SetPasswd(name string, salt, verifier []byte) error {
return nil
}
-func (a authSQLite3) Timestamp(name string) (time.Time, error) {
+func (a AuthSQLite3) Timestamp(name string) (time.Time, error) {
if err := a.init(); err != nil {
return time.Time{}, err
}
@@ -65,7 +65,7 @@ func (a authSQLite3) Timestamp(name string) (time.Time, error) {
return time.Parse("2006-01-02 15:04:05", tstr)
}
-func (a authSQLite3) Import(in []user) {
+func (a AuthSQLite3) Import(in []user) {
if err := a.init(); err != nil {
return
}
@@ -78,7 +78,7 @@ func (a authSQLite3) Import(in []user) {
}
}
-func (a authSQLite3) Export() ([]user, error) {
+func (a AuthSQLite3) Export() ([]user, error) {
if err := a.init(); err != nil {
return nil, err
}
@@ -119,11 +119,11 @@ func (a authSQLite3) Export() ([]user, error) {
return out, nil
}
-func (a authSQLite3) updateTimestamp(name string) {
+func (a AuthSQLite3) updateTimestamp(name string) {
a.db.Exec(`UPDATE user SET timestamp = datetime("now") WHERE name = ?;`, name)
}
-func (a *authSQLite3) init() error {
+func (a *AuthSQLite3) init() error {
executable, err := os.Executable()
if err != nil {
return err
@@ -148,6 +148,6 @@ func (a *authSQLite3) init() error {
return nil
}
-func (a authSQLite3) close() error {
+func (a AuthSQLite3) close() error {
return a.db.Close()
}
diff --git a/client_conn.go b/client_conn.go
index 4898518..9278578 100644
--- a/client_conn.go
+++ b/client_conn.go
@@ -1,4 +1,4 @@
-package main
+package proxy
import (
"crypto/subtle"
@@ -24,9 +24,9 @@ const (
csSudo
)
-type clientConn struct {
+type ClientConn struct {
mt.Peer
- srv *serverConn
+ srv *ServerConn
mu sync.RWMutex
cstate clientState
@@ -61,30 +61,30 @@ type clientConn struct {
modChs map[string]struct{}
}
-func (cc *clientConn) server() *serverConn {
+func (cc *ClientConn) server() *ServerConn {
cc.mu.RLock()
defer cc.mu.RUnlock()
return cc.srv
}
-func (cc *clientConn) state() clientState {
+func (cc *ClientConn) state() clientState {
cc.cstateMu.RLock()
defer cc.cstateMu.RUnlock()
return cc.cstate
}
-func (cc *clientConn) setState(state clientState) {
+func (cc *ClientConn) setState(state clientState) {
cc.cstateMu.Lock()
defer cc.cstateMu.Unlock()
cc.cstate = state
}
-func (cc *clientConn) init() <-chan struct{} { return cc.initCh }
+func (cc *ClientConn) Init() <-chan struct{} { return cc.initCh }
-func (cc *clientConn) log(dir string, v ...interface{}) {
+func (cc *ClientConn) Log(dir string, v ...interface{}) {
if cc.name != "" {
format := "{%s, %s} %s {←|⇶}"
format += strings.Repeat(" %v", len(v))
@@ -102,15 +102,15 @@ func (cc *clientConn) log(dir string, v ...interface{}) {
}
}
-func handleClt(cc *clientConn) {
+func handleClt(cc *ClientConn) {
for {
pkt, err := cc.Recv()
if err != nil {
if errors.Is(err, net.ErrClosed) {
if errors.Is(cc.WhyClosed(), rudp.ErrTimedOut) {
- cc.log("<->", "timeout")
+ cc.Log("<->", "timeout")
} else {
- cc.log("<->", "disconnect")
+ cc.Log("<->", "disconnect")
}
if cc.name != "" {
@@ -134,20 +134,20 @@ func handleClt(cc *clientConn) {
break
}
- cc.log("-->", err)
+ cc.Log("-->", err)
continue
}
switch cmd := pkt.Cmd.(type) {
case *mt.ToSrvInit:
if cc.state() > csCreated {
- cc.log("-->", "duplicate init")
+ cc.Log("-->", "duplicate init")
break
}
cc.setState(csInit)
if cmd.SerializeVer != latestSerializeVer {
- cc.log("<--", "invalid serializeVer")
+ cc.Log("<--", "invalid serializeVer")
ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.UnsupportedVer})
select {
@@ -160,7 +160,7 @@ func handleClt(cc *clientConn) {
}
if cmd.MaxProtoVer < latestProtoVer {
- cc.log("<--", "invalid protoVer")
+ cc.Log("<--", "invalid protoVer")
ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.UnsupportedVer})
select {
@@ -173,7 +173,7 @@ func handleClt(cc *clientConn) {
}
if len(cmd.PlayerName) == 0 || len(cmd.PlayerName) > maxPlayerNameLen {
- cc.log("<--", "invalid player name length")
+ cc.Log("<--", "invalid player name length")
ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.BadName})
select {
@@ -186,7 +186,7 @@ func handleClt(cc *clientConn) {
}
if ok, _ := regexp.MatchString(playerNameChars, cmd.PlayerName); !ok {
- cc.log("<--", "invalid player name")
+ cc.Log("<--", "invalid player name")
ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.BadNameChars})
select {
@@ -203,7 +203,7 @@ func handleClt(cc *clientConn) {
playersMu.Lock()
_, ok := players[cc.name]
if ok {
- cc.log("<--", "already connected")
+ cc.Log("<--", "already connected")
ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.AlreadyConnected})
select {
@@ -220,7 +220,7 @@ func handleClt(cc *clientConn) {
playersMu.Unlock()
if cc.name == "singleplayer" {
- cc.log("<--", "name is singleplayer")
+ cc.Log("<--", "name is singleplayer")
ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.BadName})
select {
@@ -233,8 +233,8 @@ func handleClt(cc *clientConn) {
}
// user limit
- if len(players) >= conf().UserLimit {
- cc.log("<--", "player limit reached")
+ if len(players) >= Conf().UserLimit {
+ cc.Log("<--", "player limit reached")
ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.TooManyClts})
select {
@@ -262,7 +262,7 @@ func handleClt(cc *clientConn) {
case *mt.ToSrvFirstSRP:
if cc.state() == csInit {
if cc.auth.method != mt.FirstSRP {
- cc.log("-->", "unauthorized password change")
+ cc.Log("-->", "unauthorized password change")
ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.UnexpectedData})
select {
@@ -279,8 +279,8 @@ func handleClt(cc *clientConn) {
salt, srpA, srpB, srpM, srpK []byte
}{}
- if cmd.EmptyPasswd && conf().RequirePasswd {
- cc.log("<--", "empty password disallowed")
+ if cmd.EmptyPasswd && Conf().RequirePasswd {
+ cc.Log("<--", "empty password disallowed")
ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.EmptyPasswd})
select {
@@ -293,7 +293,7 @@ func handleClt(cc *clientConn) {
}
if err := authIface.SetPasswd(cc.name, cmd.Salt, cmd.Verifier); err != nil {
- cc.log("<--", "set password fail")
+ cc.Log("<--", "set password fail")
ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.SrvErr})
select {
@@ -305,22 +305,22 @@ func handleClt(cc *clientConn) {
break
}
- cc.log("-->", "set password")
+ cc.Log("-->", "set password")
cc.SendCmd(&mt.ToCltAcceptAuth{
PlayerPos: mt.Pos{0, 5, 0},
MapSeed: 0,
- SendInterval: conf().SendInterval,
+ SendInterval: Conf().SendInterval,
SudoAuthMethods: mt.SRP,
})
} else {
if cc.state() < csSudo {
- cc.log("-->", "unauthorized sudo action")
+ cc.Log("-->", "unauthorized sudo action")
break
}
cc.setState(cc.state() - 1)
if err := authIface.SetPasswd(cc.name, cmd.Salt, cmd.Verifier); err != nil {
- cc.log("<--", "change password fail")
+ cc.Log("<--", "change password fail")
cc.SendCmd(&mt.ToCltChatMsg{
Type: mt.SysMsg,
Text: "Password change failed or unavailable.",
@@ -329,7 +329,7 @@ func handleClt(cc *clientConn) {
break
}
- cc.log("-->", "change password")
+ cc.Log("-->", "change password")
cc.SendCmd(&mt.ToCltChatMsg{
Type: mt.SysMsg,
Text: "Password change successful.",
@@ -340,12 +340,12 @@ func handleClt(cc *clientConn) {
wantSudo := cc.state() == csActive
if cc.state() != csInit && cc.state() != csActive {
- cc.log("-->", "unexpected authentication")
+ cc.Log("-->", "unexpected authentication")
break
}
if !wantSudo && cc.auth.method != mt.SRP {
- cc.log("<--", "multiple authentication attempts")
+ cc.Log("<--", "multiple authentication attempts")
if wantSudo {
cc.SendCmd(&mt.ToCltDenySudoMode{})
break
@@ -362,7 +362,7 @@ func handleClt(cc *clientConn) {
}
if !cmd.NoSHA1 {
- cc.log("<--", "unsupported SHA1 auth")
+ cc.Log("<--", "unsupported SHA1 auth")
break
}
@@ -370,7 +370,7 @@ func handleClt(cc *clientConn) {
salt, verifier, err := authIface.Passwd(cc.name)
if err != nil {
- cc.log("<--", "SRP data retrieval fail")
+ cc.Log("<--", "SRP data retrieval fail")
ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.SrvErr})
select {
@@ -386,7 +386,7 @@ func handleClt(cc *clientConn) {
cc.auth.srpA = cmd.A
cc.auth.srpB, _, cc.auth.srpK, err = srp.Handshake(cc.auth.srpA, verifier)
if err != nil || cc.auth.srpB == nil {
- cc.log("<--", "SRP safety check fail")
+ cc.Log("<--", "SRP safety check fail")
ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.UnexpectedData})
select {
@@ -406,12 +406,12 @@ func handleClt(cc *clientConn) {
wantSudo := cc.state() == csActive
if cc.state() != csInit && cc.state() != csActive {
- cc.log("-->", "unexpected authentication")
+ cc.Log("-->", "unexpected authentication")
break
}
if cc.auth.method != mt.SRP {
- cc.log("<--", "multiple authentication attempts")
+ cc.Log("<--", "multiple authentication attempts")
if wantSudo {
cc.SendCmd(&mt.ToCltDenySudoMode{})
break
@@ -442,18 +442,18 @@ func handleClt(cc *clientConn) {
cc.SendCmd(&mt.ToCltAcceptAuth{
PlayerPos: mt.Pos{0, 5, 0},
MapSeed: 0,
- SendInterval: conf().SendInterval,
+ SendInterval: Conf().SendInterval,
SudoAuthMethods: mt.SRP,
})
}
} else {
if wantSudo {
- cc.log("<--", "invalid password (sudo)")
+ cc.Log("<--", "invalid password (sudo)")
cc.SendCmd(&mt.ToCltDenySudoMode{})
break
}
- cc.log("<--", "invalid password")
+ cc.Log("<--", "invalid password")
ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.WrongPasswd})
select {
@@ -467,7 +467,7 @@ func handleClt(cc *clientConn) {
case *mt.ToSrvInit2:
cc.itemDefs, cc.aliases, cc.nodeDefs, cc.p0Map, cc.p0SrvMap, cc.media, err = muxContent(cc.name)
if err != nil {
- cc.log("<--", err.Error())
+ cc.Log("<--", err.Error())
ack, _ := cc.SendCmd(&mt.ToCltDisco{
Reason: mt.Custom,
@@ -502,28 +502,28 @@ func handleClt(cc *clientConn) {
cc.lang = cmd.Lang
var csmrf mt.CSMRestrictionFlags
- if conf().CSMRF.NoCSMs {
+ if Conf().CSMRF.NoCSMs {
csmrf |= mt.NoCSMs
}
- if !conf().CSMRF.ChatMsgs {
+ if !Conf().CSMRF.ChatMsgs {
csmrf |= mt.NoChatMsgs
}
- if !conf().CSMRF.ItemDefs {
+ if !Conf().CSMRF.ItemDefs {
csmrf |= mt.NoItemDefs
}
- if !conf().CSMRF.NodeDefs {
+ if !Conf().CSMRF.NodeDefs {
csmrf |= mt.NoNodeDefs
}
- if !conf().CSMRF.NoLimitMapRange {
+ if !Conf().CSMRF.NoLimitMapRange {
csmrf |= mt.LimitMapRange
}
- if !conf().CSMRF.PlayerList {
+ if !Conf().CSMRF.PlayerList {
csmrf |= mt.NoPlayerList
}
cc.SendCmd(&mt.ToCltCSMRestrictionFlags{
Flags: csmrf,
- MapRange: conf().MapRange,
+ MapRange: Conf().MapRange,
})
case *mt.ToSrvReqMedia:
cc.sendMedia(cmd.Filenames)
@@ -539,7 +539,7 @@ func handleClt(cc *clientConn) {
close(cc.initCh)
case *mt.ToSrvInteract:
if cc.server() == nil {
- cc.log("-->", "no server")
+ cc.Log("-->", "no server")
break
}
@@ -550,32 +550,32 @@ func handleClt(cc *clientConn) {
cc.server().SendCmd(cmd)
case *mt.ToSrvChatMsg:
if cc.server() == nil {
- cc.log("-->", "no server")
+ cc.Log("-->", "no server")
break
}
var handled bool
- if strings.HasPrefix(cmd.Msg, conf().CmdPrefix) {
+ if strings.HasPrefix(cmd.Msg, Conf().CmdPrefix) {
substrs := strings.Split(cmd.Msg, " ")
- cmdName := strings.Replace(substrs[0], conf().CmdPrefix, "", 1)
+ cmdName := strings.Replace(substrs[0], Conf().CmdPrefix, "", 1)
var args []string
if len(substrs) > 1 {
args = substrs[1:]
}
- cc.log("-->", append([]string{"cmd", cmdName}, args...))
+ cc.Log("-->", append([]string{"cmd", cmdName}, args...))
pluginsMu.RLock()
for _, p := range plugins {
sym, err := p.Lookup("HandleChatCmd")
if err != nil {
- cc.log("-->", err)
+ cc.Log("-->", err)
continue
}
- if handler, ok := sym.(func(string, []string) bool); ok {
- if handler(cmdName, args) {
+ if handler, ok := sym.(func(*ClientConn, string, ...string) bool); ok {
+ if handler(cc, cmdName, args...) {
handled = true
break
}
@@ -598,73 +598,73 @@ func handleClt(cc *clientConn) {
}
case *mt.ToSrvDeletedBlks:
if cc.server() == nil {
- cc.log("-->", "no server")
+ cc.Log("-->", "no server")
break
}
cc.server().SendCmd(cmd)
case *mt.ToSrvFallDmg:
if cc.server() == nil {
- cc.log("-->", "no server")
+ cc.Log("-->", "no server")
break
}
cc.server().SendCmd(cmd)
case *mt.ToSrvGotBlks:
if cc.server() == nil {
- cc.log("-->", "no server")
+ cc.Log("-->", "no server")
break
}
cc.server().SendCmd(cmd)
case *mt.ToSrvJoinModChan:
if cc.server() == nil {
- cc.log("-->", "no server")
+ cc.Log("-->", "no server")
break
}
cc.server().SendCmd(cmd)
case *mt.ToSrvLeaveModChan:
if cc.server() == nil {
- cc.log("-->", "no server")
+ cc.Log("-->", "no server")
break
}
cc.server().SendCmd(cmd)
case *mt.ToSrvMsgModChan:
if cc.server() == nil {
- cc.log("-->", "no server")
+ cc.Log("-->", "no server")
break
}
cc.server().SendCmd(cmd)
case *mt.ToSrvNodeMetaFields:
if cc.server() == nil {
- cc.log("-->", "no server")
+ cc.Log("-->", "no server")
break
}
cc.server().SendCmd(cmd)
case *mt.ToSrvPlayerPos:
if cc.server() == nil {
- cc.log("-->", "no server")
+ cc.Log("-->", "no server")
break
}
cc.server().SendCmd(cmd)
case *mt.ToSrvRespawn:
if cc.server() == nil {
- cc.log("-->", "no server")
+ cc.Log("-->", "no server")
break
}
cc.server().SendCmd(cmd)
case *mt.ToSrvInvAction:
if cc.server() == nil {
- cc.log("-->", "no server")
+ cc.Log("-->", "no server")
break
}
cc.server().SendCmd(cmd)
case *mt.ToSrvInvFields:
if cc.server() == nil {
- cc.log("-->", "no server")
+ cc.Log("-->", "no server")
break
}
cc.server().SendCmd(cmd)
case *mt.ToSrvSelectItem:
if cc.server() == nil {
- cc.log("-->", "no server")
+ cc.Log("-->", "no server")
break
}
cc.server().SendCmd(cmd)
diff --git a/main.go b/cmd/mt-multiserver-proxy/main.go
index c18b387..e1b2b19 100644
--- a/main.go
+++ b/cmd/mt-multiserver-proxy/main.go
@@ -9,29 +9,30 @@ import (
"sync"
"syscall"
+ "github.com/HimbeerserverDE/mt-multiserver-proxy"
"github.com/anon55555/mt"
)
func main() {
- if err := loadConfig(); err != nil {
+ if err := proxy.LoadConfig(); err != nil {
log.Fatal("{←|⇶} ", err)
}
- if !conf().NoPlugins {
- if err := loadPlugins(); err != nil {
+ if !proxy.Conf().NoPlugins {
+ if err := proxy.LoadPlugins(); err != nil {
log.Fatal("{←|⇶} ", err)
}
}
var err error
- switch conf().AuthBackend {
+ switch proxy.Conf().AuthBackend {
case "sqlite3":
- authIface = authSQLite3{}
+ proxy.SetAuthBackend(proxy.AuthSQLite3{})
default:
log.Fatal("{←|⇶} invalid auth backend")
}
- addr, err := net.ResolveUDPAddr("udp", conf().BindAddr)
+ addr, err := net.ResolveUDPAddr("udp", proxy.Conf().BindAddr)
if err != nil {
log.Fatal("{←|⇶} ", err)
}
@@ -41,24 +42,23 @@ func main() {
log.Fatal("{←|⇶} ", err)
}
- l := listen(pc)
- defer l.close()
+ l := proxy.Listen(pc)
+ defer l.Close()
- log.Print("{←|⇶} listen ", l.addr())
+ log.Print("{←|⇶} listen ", l.Addr())
go func() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
<-sig
- l.mu.Lock()
- defer l.mu.Unlock()
+ clts := l.Clts()
var wg sync.WaitGroup
- wg.Add(len(l.clts))
+ wg.Add(len(clts))
- for cc := range l.clts {
- go func(cc *clientConn) {
+ for cc := range clts {
+ go func(cc *proxy.ClientConn) {
ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.Shutdown})
select {
case <-cc.Closed():
@@ -66,11 +66,6 @@ func main() {
cc.Close()
}
- <-cc.server().Closed()
- cc.mu.Lock()
- cc.srv = nil
- cc.mu.Unlock()
-
wg.Done()
}(cc)
}
@@ -80,7 +75,7 @@ func main() {
}()
for {
- cc, err := l.accept()
+ cc, err := l.Accept()
if err != nil {
if errors.Is(err, net.ErrClosed) {
log.Print("{←|⇶} stop listening")
@@ -92,11 +87,11 @@ func main() {
}
go func() {
- <-cc.init()
- cc.log("<->", "handshake completed")
+ <-cc.Init()
+ cc.Log("<->", "handshake completed")
- if len(conf().Servers) == 0 {
- cc.log("<--", "no servers")
+ if len(proxy.Conf().Servers) == 0 {
+ cc.Log("<--", "no servers")
ack, _ := cc.SendCmd(&mt.ToCltDisco{
Reason: mt.Custom,
Custom: "No servers are configured.",
@@ -110,9 +105,9 @@ func main() {
return
}
- addr, err := net.ResolveUDPAddr("udp", conf().Servers[0].Addr)
+ addr, err := net.ResolveUDPAddr("udp", proxy.Conf().Servers[0].Addr)
if err != nil {
- cc.log("<--", "address resolution fail")
+ cc.Log("<--", "address resolution fail")
ack, _ := cc.SendCmd(&mt.ToCltDisco{
Reason: mt.Custom,
Custom: "Server address resolution failed.",
@@ -128,7 +123,7 @@ func main() {
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
- cc.log("<--", "connection fail")
+ cc.Log("<--", "connection fail")
ack, _ := cc.SendCmd(&mt.ToCltDisco{
Reason: mt.Custom,
@@ -144,7 +139,7 @@ func main() {
return
}
- connect(conn, conf().Servers[0].Name, cc)
+ proxy.Connect(conn, proxy.Conf().Servers[0].Name, cc)
}()
}
diff --git a/config.go b/config.go
index 2cd4efe..50653bc 100644
--- a/config.go
+++ b/config.go
@@ -1,4 +1,4 @@
-package main
+package proxy
import (
"encoding/json"
@@ -46,14 +46,14 @@ type Config struct {
MapRange uint32
}
-func conf() Config {
+func Conf() Config {
configMu.RLock()
defer configMu.RUnlock()
return config
}
-func loadConfig() error {
+func LoadConfig() error {
configMu.Lock()
defer configMu.Unlock()
diff --git a/connect.go b/connect.go
index d2a376a..4aa7a5d 100644
--- a/connect.go
+++ b/connect.go
@@ -1,4 +1,4 @@
-package main
+package proxy
import (
"net"
@@ -6,8 +6,16 @@ import (
"github.com/anon55555/mt"
)
-func connect(conn net.Conn, name string, cc *clientConn) *serverConn {
- sc := &serverConn{
+func Connect(conn net.Conn, name string, cc *ClientConn) *ServerConn {
+ cc.mu.RLock()
+ if cc.srv != nil {
+ cc.Log("<->", "already connected to server")
+ cc.mu.RUnlock()
+ return nil
+ }
+ cc.mu.RUnlock()
+
+ sc := &ServerConn{
Peer: mt.Connect(conn),
initCh: make(chan struct{}),
clt: cc,
@@ -18,7 +26,7 @@ func connect(conn net.Conn, name string, cc *clientConn) *serverConn {
huds: make(map[mt.HUDID]mt.HUDType),
playerList: make(map[string]struct{}),
}
- sc.log("-->", "connect")
+ sc.Log("-->", "connect")
cc.mu.Lock()
cc.srv = sc
diff --git a/content.go b/content.go
index 77a58f6..849691f 100644
--- a/content.go
+++ b/content.go
@@ -1,4 +1,4 @@
-package main
+package proxy
import (
"encoding/base64"
@@ -235,7 +235,7 @@ func handleContent(cc *contentConn) {
}
}
-func (cc *clientConn) sendMedia(filenames []string) {
+func (cc *ClientConn) sendMedia(filenames []string) {
var bunches [][]struct {
Name string
Data []byte
@@ -274,7 +274,7 @@ func (cc *clientConn) sendMedia(filenames []string) {
}
if !known {
- cc.log("-->", "request unknown media file")
+ cc.Log("-->", "request unknown media file")
continue
}
}
@@ -435,7 +435,7 @@ func muxMedia(conns []*contentConn) []mediaFile {
func muxContent(userName string) (itemDefs []mt.ItemDef, aliases []struct{ Alias, Orig string }, nodeDefs []mt.NodeDef, p0Map param0Map, p0SrvMap param0SrvMap, media []mediaFile, err error) {
var conns []*contentConn
- for _, srv := range conf().Servers {
+ for _, srv := range Conf().Servers {
var addr *net.UDPAddr
addr, err = net.ResolveUDPAddr("udp", srv.Addr)
if err != nil {
@@ -464,7 +464,7 @@ func muxContent(userName string) (itemDefs []mt.ItemDef, aliases []struct{ Alias
return
}
-func (sc *serverConn) globalParam0(p0 *mt.Content) {
+func (sc *ServerConn) globalParam0(p0 *mt.Content) {
clt := sc.client()
if clt != nil && clt.p0Map != nil {
if clt.p0Map[sc.name] != nil {
@@ -473,7 +473,7 @@ func (sc *serverConn) globalParam0(p0 *mt.Content) {
}
}
-func (cc *clientConn) srvParam0(p0 *mt.Content) string {
+func (cc *ClientConn) srvParam0(p0 *mt.Content) string {
if cc.p0SrvMap != nil {
srv := cc.p0SrvMap[*p0]
*p0 = srv.param0
@@ -532,7 +532,7 @@ func prependTexture(prep string, t *mt.Texture) {
*t = mt.Texture(s)
}
-func (sc *serverConn) prependInv(inv mt.Inv) {
+func (sc *ServerConn) prependInv(inv mt.Inv) {
for k, l := range inv {
for i := range l.Stacks {
prepend(sc.name, &inv[k].InvList.Stacks[i].Name)
@@ -540,7 +540,7 @@ func (sc *serverConn) prependInv(inv mt.Inv) {
}
}
-func (sc *serverConn) prependHUD(t mt.HUDType, cmdIface mt.ToCltCmd) {
+func (sc *ServerConn) prependHUD(t mt.HUDType, cmdIface mt.ToCltCmd) {
pa := func(cmd *mt.ToCltAddHUD) {
switch t {
case mt.StatbarHUD:
diff --git a/default_textures.go b/default_textures.go
index 7cc5330..148868c 100644
--- a/default_textures.go
+++ b/default_textures.go
@@ -1,4 +1,4 @@
-package main
+package proxy
var defaultTextures = []mediaFile{
mediaFile{
diff --git a/formspec.go b/formspec.go
index 5a96b74..12553f1 100644
--- a/formspec.go
+++ b/formspec.go
@@ -1,11 +1,11 @@
-package main
+package proxy
import (
"regexp"
"strings"
)
-func (sc *serverConn) prependFormspec(fs *string) {
+func (sc *ServerConn) prependFormspec(fs *string) {
reg := regexp.MustCompile("[^a-zA-Z0-9-_.:]")
reg2 := regexp.MustCompile("[a-zA-Z0-9-_.]*\\.[a-zA-Z-_.]+")
subs := reg.Split(*fs, -1)
diff --git a/gen_textures.go b/gen_textures.go
index 3712096..f2a9517 100644
--- a/gen_textures.go
+++ b/gen_textures.go
@@ -23,7 +23,7 @@ func main() {
}
defer f.Close()
- f.WriteString("package main\n")
+ f.WriteString("package proxy\n")
f.WriteString("\n")
f.WriteString("var defaultTextures = []mediaFile{\n")
diff --git a/hop.go b/hop.go
index 26beb3e..4aabb1e 100644
--- a/hop.go
+++ b/hop.go
@@ -1,4 +1,4 @@
-package main
+package proxy
import (
"fmt"
@@ -8,20 +8,20 @@ import (
"github.com/anon55555/mt"
)
-func (cc *clientConn) hop(serverName string) error {
+func (cc *ClientConn) hop(serverName string) error {
cc.hopMu.Lock()
defer cc.hopMu.Unlock()
- cc.log("<->", "hop", serverName)
+ cc.Log("<->", "hop", serverName)
if cc.server() == nil {
err := fmt.Errorf("no server connection")
- cc.log("<->", err)
+ cc.Log("<->", err)
return err
}
var strAddr string
- for _, srv := range conf().Servers {
+ for _, srv := range Conf().Servers {
if srv.Name == serverName {
strAddr = srv.Addr
break
@@ -145,7 +145,7 @@ func (cc *clientConn) hop(serverName string) error {
return err
}
- connect(conn, serverName, cc)
+ Connect(conn, serverName, cc)
for ch := range cc.modChs {
cc.server().SendCmd(&mt.ToSrvJoinModChan{Channel: ch})
diff --git a/listen.go b/listen.go
index 2ea3f28..d51940a 100644
--- a/listen.go
+++ b/listen.go
@@ -1,4 +1,4 @@
-package main
+package proxy
import (
"fmt"
@@ -8,33 +8,40 @@ import (
"github.com/anon55555/mt"
)
-type listener struct {
- mtListener mt.Listener
- mu sync.Mutex
+type Listener struct {
+ mt.Listener
+ mu sync.RWMutex
- clts map[*clientConn]struct{}
+ clts map[*ClientConn]struct{}
}
-func listen(pc net.PacketConn) *listener {
- return &listener{
- mtListener: mt.Listen(pc),
- clts: make(map[*clientConn]struct{}),
+func Listen(pc net.PacketConn) *Listener {
+ return &Listener{
+ Listener: mt.Listen(pc),
+ clts: make(map[*ClientConn]struct{}),
}
}
-func (l *listener) close() error {
- return l.mtListener.Close()
-}
+func (l *Listener) Clts() map[*ClientConn]struct{} {
+ clts := make(map[*ClientConn]struct{})
+
+ l.mu.RLock()
+ defer l.mu.RUnlock()
-func (l *listener) addr() net.Addr { return l.mtListener.Addr() }
+ for cc := range l.clts {
+ clts[cc] = struct{}{}
+ }
+
+ return clts
+}
-func (l *listener) accept() (*clientConn, error) {
- p, err := l.mtListener.Accept()
+func (l *Listener) Accept() (*ClientConn, error) {
+ p, err := l.Listener.Accept()
if err != nil {
return nil, err
}
- cc := &clientConn{
+ cc := &ClientConn{
Peer: p,
initCh: make(chan struct{}),
modChs: make(map[string]struct{}),
@@ -52,7 +59,7 @@ func (l *listener) accept() (*clientConn, error) {
delete(l.clts, cc)
}()
- cc.log("-->", "connect")
+ cc.Log("-->", "connect")
go handleClt(cc)
select {
diff --git a/log.go b/log.go
index f4e25be..2e16a73 100644
--- a/log.go
+++ b/log.go
@@ -1,4 +1,4 @@
-package main
+package proxy
import (
"log"
diff --git a/players.go b/players.go
index 183a405..f4b2b0c 100644
--- a/players.go
+++ b/players.go
@@ -1,4 +1,4 @@
-package main
+package proxy
import "sync"
diff --git a/plugin.go b/plugin.go
index d4e3d27..2f7a803 100644
--- a/plugin.go
+++ b/plugin.go
@@ -1,4 +1,4 @@
-package main
+package proxy
import (
"log"
@@ -11,7 +11,7 @@ import (
var plugins []*plugin.Plugin
var pluginsMu sync.RWMutex
-func loadPlugins() error {
+func LoadPlugins() error {
executable, err := os.Executable()
if err != nil {
return err
diff --git a/server_conn.go b/server_conn.go
index 673429f..bc89061 100644
--- a/server_conn.go
+++ b/server_conn.go
@@ -1,4 +1,4 @@
-package main
+package proxy
import (
"errors"
@@ -14,9 +14,9 @@ import (
"github.com/anon55555/mt/rudp"
)
-type serverConn struct {
+type ServerConn struct {
mt.Peer
- clt *clientConn
+ clt *ClientConn
mu sync.RWMutex
cstate clientState
@@ -42,35 +42,35 @@ type serverConn struct {
playerList map[string]struct{}
}
-func (sc *serverConn) client() *clientConn {
+func (sc *ServerConn) client() *ClientConn {
sc.mu.RLock()
defer sc.mu.RUnlock()
return sc.clt
}
-func (sc *serverConn) state() clientState {
+func (sc *ServerConn) state() clientState {
sc.cstateMu.RLock()
defer sc.cstateMu.RUnlock()
return sc.cstate
}
-func (sc *serverConn) setState(state clientState) {
+func (sc *ServerConn) setState(state clientState) {
sc.cstateMu.Lock()
defer sc.cstateMu.Unlock()
sc.cstate = state
}
-func (sc *serverConn) init() <-chan struct{} { return sc.initCh }
+func (sc *ServerConn) Init() <-chan struct{} { return sc.initCh }
-func (sc *serverConn) log(dir string, v ...interface{}) {
+func (sc *ServerConn) Log(dir string, v ...interface{}) {
if sc.client() != nil {
format := "%s {%s}"
format += strings.Repeat(" %v", len(v))
- sc.client().log("", fmt.Sprintf(format, append([]interface{}{
+ sc.client().Log("", fmt.Sprintf(format, append([]interface{}{
dir,
sc.name,
}, v...)...))
@@ -82,7 +82,7 @@ func (sc *serverConn) log(dir string, v ...interface{}) {
}
}
-func handleSrv(sc *serverConn) {
+func handleSrv(sc *ServerConn) {
go func() {
init := make(chan struct{})
defer close(init)
@@ -91,7 +91,7 @@ func handleSrv(sc *serverConn) {
select {
case <-init:
case <-time.After(10 * time.Second):
- sc.log("-->", "timeout")
+ sc.Log("-->", "timeout")
sc.Close()
}
}(init)
@@ -112,9 +112,9 @@ func handleSrv(sc *serverConn) {
if err != nil {
if errors.Is(err, net.ErrClosed) {
if errors.Is(sc.WhyClosed(), rudp.ErrTimedOut) {
- sc.log("<->", "timeout")
+ sc.Log("<->", "timeout")
} else {
- sc.log("<->", "disconnect")
+ sc.Log("<->", "disconnect")
}
if sc.client() != nil {
@@ -141,20 +141,20 @@ func handleSrv(sc *serverConn) {
break
}
- sc.log("<--", err)
+ sc.Log("<--", err)
continue
}
clt := sc.client()
if clt == nil {
- sc.log("<--", "no client")
+ sc.Log("<--", "no client")
continue
}
switch cmd := pkt.Cmd.(type) {
case *mt.ToCltHello:
if sc.auth.method != 0 {
- sc.log("<--", "unexpected authentication")
+ sc.Log("<--", "unexpected authentication")
sc.Close()
break
}
@@ -167,7 +167,7 @@ func handleSrv(sc *serverConn) {
}
if cmd.SerializeVer != latestSerializeVer {
- sc.log("<--", "invalid serializeVer")
+ sc.Log("<--", "invalid serializeVer")
break
}
@@ -175,7 +175,7 @@ func handleSrv(sc *serverConn) {
case mt.SRP:
sc.auth.srpA, sc.auth.a, err = srp.InitiateHandshake()
if err != nil {
- sc.log("-->", err)
+ sc.Log("-->", err)
break
}
@@ -186,7 +186,7 @@ func handleSrv(sc *serverConn) {
case mt.FirstSRP:
salt, verifier, err := srp.NewClient([]byte(clt.name), []byte{})
if err != nil {
- sc.log("-->", err)
+ sc.Log("-->", err)
break
}
@@ -196,24 +196,24 @@ func handleSrv(sc *serverConn) {
EmptyPasswd: true,
})
default:
- sc.log("<->", "invalid auth method")
+ sc.Log("<->", "invalid auth method")
sc.Close()
}
case *mt.ToCltSRPBytesSaltB:
if sc.auth.method != mt.SRP {
- sc.log("<--", "multiple authentication attempts")
+ sc.Log("<--", "multiple authentication attempts")
break
}
sc.auth.srpK, err = srp.CompleteHandshake(sc.auth.srpA, sc.auth.a, []byte(clt.name), []byte{}, cmd.Salt, cmd.B)
if err != nil {
- sc.log("-->", err)
+ sc.Log("-->", err)
break
}
M := srp.ClientProof([]byte(clt.name), cmd.Salt, sc.auth.srpA, cmd.B, sc.auth.srpK)
if M == nil {
- sc.log("<--", "SRP safety check fail")
+ sc.Log("<--", "SRP safety check fail")
break
}
@@ -221,7 +221,7 @@ func handleSrv(sc *serverConn) {
M: M,
})
case *mt.ToCltDisco:
- sc.log("<--", "deny access", cmd)
+ sc.Log("<--", "deny access", cmd)
ack, _ := clt.SendCmd(cmd)
select {
@@ -240,9 +240,9 @@ func handleSrv(sc *serverConn) {
}{}
sc.SendCmd(&mt.ToSrvInit2{Lang: clt.lang})
case *mt.ToCltDenySudoMode:
- sc.log("<--", "deny sudo")
+ sc.Log("<--", "deny sudo")
case *mt.ToCltAcceptSudoMode:
- sc.log("<--", "accept sudo")
+ sc.Log("<--", "accept sudo")
sc.setState(sc.state() + 1)
case *mt.ToCltAnnounceMedia:
sc.SendCmd(&mt.ToSrvReqMedia{})
@@ -256,7 +256,7 @@ func handleSrv(sc *serverConn) {
Formspec: clt.formspecVer,
})
- sc.log("<->", "handshake completed")
+ sc.Log("<->", "handshake completed")
sc.setState(sc.state() + 1)
close(sc.initCh)
case *mt.ToCltInv: