diff options
-rw-r--r-- | client_conn.go | 3 | ||||
-rw-r--r-- | config.go | 30 | ||||
-rw-r--r-- | connect.go | 11 | ||||
-rw-r--r-- | doc/config.md | 23 | ||||
-rw-r--r-- | doc/server_groups.md | 2 | ||||
-rw-r--r-- | fallback.go | 61 | ||||
-rw-r--r-- | hop.go | 23 | ||||
-rw-r--r-- | process.go | 12 | ||||
-rw-r--r-- | run.go | 16 | ||||
-rw-r--r-- | server_conn.go | 44 |
10 files changed, 116 insertions, 109 deletions
diff --git a/client_conn.go b/client_conn.go index 951fa95..9753147 100644 --- a/client_conn.go +++ b/client_conn.go @@ -40,6 +40,9 @@ type ClientConn struct { salt, srpA, srpB, srpM, srpK []byte } + fallbackFrom string + whyKicked *mt.ToCltKick + lang string major, minor, patch uint8 @@ -29,7 +29,7 @@ type Server struct { Addr string MediaPool string Groups []string - Fallbacks []string + Fallback string dynamic bool poolAdded time.Time @@ -53,7 +53,6 @@ type Config struct { Servers map[string]Server ForceDefaultSrv bool KickOnNewPool bool - FallbackServers []string CSMRF struct { NoCSMs bool ChatMsgs bool @@ -165,9 +164,6 @@ func (cnf Config) clone() Config { newConfig.Servers = copyMap(cnf.Servers) - newConfig.FallbackServers = make([]string, len(cnf.FallbackServers)) - copy(newConfig.FallbackServers, cnf.FallbackServers) - newConfig.Groups = copyMapSlice(cnf.Groups) newConfig.UserGroups = copyMap(cnf.UserGroups) @@ -286,29 +282,6 @@ func (cnf Config) RandomGroupServer(search string) (string, bool) { return candidates[rand.Intn(len(candidates))], true } -// FallbackServers returns a slice of server names that -// a server can fall back to. -func FallbackServers(server string) []string { - conf := Conf() - - srv, ok := conf.Servers[server] - if !ok { - return nil - } - - fallbacks := srv.Fallbacks - fallbacks = append(fallbacks, conf.FallbackServers...) - - final := make([]string, 0, len(fallbacks)) - for _, srvName := range fallbacks { - if srvName != server { - final = append(final, srvName) - } - } - - return final -} - // LoadConfig attempts to parse the configuration file. // It leaves the config unchanged if there is an error // and returns the error. @@ -325,7 +298,6 @@ func LoadConfig() error { config.TelnetAddr = defaultTelnetAddr config.BindAddr = defaultBindAddr config.Servers = make(map[string]Server) - config.FallbackServers = make([]string, 0) config.Groups = make(map[string][]string) config.UserGroups = make(map[string]string) config.List.Interval = defaultListInterval @@ -4,6 +4,7 @@ import ( "fmt" "log" "net" + "time" "github.com/HimbeerserverDE/mt" ) @@ -42,8 +43,18 @@ func connect(conn net.Conn, name string, cc *ClientConn) *ServerConn { cc.mu.Lock() cc.srv = sc + cc.fallbackFrom = sc.name cc.mu.Unlock() + // Mark fallback as done after a connection has persisted for some time. + go func() { + time.Sleep(10 * time.Second) + + if cc.srv == sc { + cc.whyKicked = nil + } + }() + go handleSrv(sc) return sc } diff --git a/doc/config.md b/doc/config.md index 18a4006..e9e4e01 100644 --- a/doc/config.md +++ b/doc/config.md @@ -140,15 +140,13 @@ See [server_groups.md](https://github.com/HimbeerserverDE/mt-multiserver-proxy/b for more information. ``` -> `Server.Fallbacks` +> `Server.Fallback` ``` -Type: []string -Default: []string{} -Description: The names of the servers a client should fall back to -if this server shuts down or crashes gracefully. Connection attempts -are made in the order in which the servers are given. As soon as -a connection is successful the other fallback servers in this list -will be ignored. +Type: string +Default: "" +Description: The name of the server a client should fall back to if this server +shuts down or crashes gracefully. Connection attempts are made in the order in +which the servers are given. ``` > `ForceDefaultSrv` @@ -224,15 +222,6 @@ Default: 0 Description: The maximum distance from which CSMs can read the map. ``` -> `FallbackServers` -``` -Type: []string -Default: []string{} -Description: Names of general fallback servers to connect to -if a connection attempt fails or an existing connection -to a game server is lost. -``` - > `DropCSMRF` ``` Type: bool diff --git a/doc/server_groups.md b/doc/server_groups.md index 4a0140a..14b9b86 100644 --- a/doc/server_groups.md +++ b/doc/server_groups.md @@ -5,7 +5,7 @@ in the `Groups` subfield of the server definition in the config. Configuration options that support server groups will randomly choose from their member servers every time they are applied to a client. -Neither local nor global fallback servers can be server groups. +Fallback servers cannot be server groups. If there is a server group with the same name as a regular server, the regular server is preferred, rendering the group inaccessible. diff --git a/fallback.go b/fallback.go new file mode 100644 index 0000000..729a4e4 --- /dev/null +++ b/fallback.go @@ -0,0 +1,61 @@ +package proxy + +import "github.com/HimbeerserverDE/mt" + +func (cc *ClientConn) fallback() bool { + if cc.fallbackFrom == "" { + return false + } + + fallback := config.Servers[cc.fallbackFrom].Fallback + if fallback == "" { + ack, _ := cc.SendCmd(cc.whyKicked) + + select { + case <-cc.Closed(): + case <-ack: + cc.Close() + + cc.mu.Lock() + cc.srv = nil + cc.mu.Unlock() + + if srv := cc.server(); srv != nil { + srv.mu.Lock() + srv.clt = nil + srv.mu.Unlock() + } + } + + return true + } + + // Use HopRaw so the fallback server doesn't get saved + // as the last server. + if err := cc.HopRaw(fallback); err != nil { + cc.Log("<-", "fallback fail:", err) + + ack, _ := cc.SendCmd(&mt.ToCltKick{ + Reason: mt.Custom, + Custom: "Fallback failed.", + }) + + select { + case <-cc.Closed(): + case <-ack: + cc.Close() + + cc.mu.Lock() + cc.srv = nil + cc.mu.Unlock() + + if srv := cc.server(); srv != nil { + srv.mu.Lock() + srv.clt = nil + srv.mu.Unlock() + } + } + } + + return true +} @@ -28,27 +28,8 @@ func (cc *ClientConn) Hop(serverName string) (err error) { } }() - if err = cc.HopRaw(serverName); err != nil { - if errors.Is(err, ErrNoSuchServer) || errors.Is(err, ErrNewMediaPool) { - return err - } - - cc.Log("<-", err) - cc.SendChatMsg("Could not switch servers, triggering fallback. Error:", err.Error()) - - for _, srvName := range FallbackServers(serverName) { - if err = cc.HopRaw(srvName); err != nil { - cc.Log("<-", err) - cc.SendChatMsg("Could not connect, continuing fallback. Error:", err.Error()) - } - - return nil - } - - return err - } - - return nil + err = cc.HopRaw(serverName) + return } // HopGroup connects the ClientConn to the specified server group @@ -568,16 +568,12 @@ func (sc *ServerConn) process(pkt mt.Pkt) { sc.Log("<-", "deny access", cmd) if cmd.Reason == mt.Shutdown || cmd.Reason == mt.Crash || cmd.Reason == mt.SrvErr || cmd.Reason == mt.TooManyClts || cmd.Reason == mt.UnsupportedVer { - clt.SendChatMsg("A kick occured, triggering fallback. Reason:", cmd.String()) + clt.SendChatMsg("A kick occured, switching to fallback server. Reason:", cmd) - for _, srvName := range FallbackServers(sc.name) { - if err := clt.HopRaw(srvName); err != nil { - clt.Log("<-", err) - clt.SendChatMsg("Could not connect to "+srvName+", continuing fallback. Error:", err.Error()) - } + clt.whyKicked = cmd - return - } + clt.fallback() + return } ack, _ := clt.SendCmd(cmd) @@ -156,18 +156,24 @@ func runFunc() { if err := doConnect(srvName, srv); err != nil { cc.Log("<-", err) - cc.SendChatMsg("Could not connect, triggering fallback. Error:", err.Error()) + cc.SendChatMsg("Could not connect, trying fallback server. Error:", err) - for _, fbName := range FallbackServers(srvName) { - fb, ok := conf.Servers[fbName] + fbName := srv.Fallback + for fbName != "" { + var ok bool + srv, ok = conf.Servers[fbName] if !ok { cc.Log("<-", "invalid fallback") continue } - if err := doConnect(fbName, fb); err != nil { + if err := doConnect(fbName, srv); err != nil { + fbName = srv.Fallback + cc.Log("<-", err) - cc.SendChatMsg("Could not connect, continuing fallback. Error:", err.Error()) + cc.SendChatMsg("Could not connect, trying next fallback server. Error:", err.Error()) + } else { + break } } } diff --git a/server_conn.go b/server_conn.go index ec3164f..f451cd5 100644 --- a/server_conn.go +++ b/server_conn.go @@ -104,7 +104,6 @@ func handleSrv(sc *ServerConn) { } }() -RecvLoop: for { pkt, err := sc.Recv() if err != nil { @@ -117,38 +116,27 @@ RecvLoop: if sc.client() != nil { if errors.Is(sc.WhyClosed(), rudp.ErrTimedOut) { - sc.client().SendChatMsg("Server connection timed out, triggering fallback.") - } else { - sc.client().SendChatMsg("Server connection lost, triggering fallback.") - } + sc.client().SendChatMsg("Server connection timed out, switching to fallback server.") - for _, srvName := range FallbackServers(sc.name) { - if err := sc.client().HopRaw(srvName); err != nil { - sc.client().Log("<-", err) - sc.client().SendChatMsg("Could not connect to "+srvName+", continuing fallback. Error:", err.Error()) + if sc.client().whyKicked == nil { + sc.client().whyKicked = &mt.ToCltKick{ + Reason: mt.Custom, + Custom: "Server connection timed out.", + } } + } else { + sc.client().SendChatMsg("Server connection lost, switching to fallback server.") - break RecvLoop + if sc.client().whyKicked == nil { + sc.client().whyKicked = &mt.ToCltKick{ + Reason: mt.Custom, + Custom: "Server connection lost.", + } + } } - 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() - } + sc.client().fallback() + break } break |