Skip to content

Commit

Permalink
initial server group implementation
Browse files Browse the repository at this point in the history
Not a full implementation of #142 yet: default server isn't handled as it needs to be refactored separately (#140).
  • Loading branch information
HimbeerserverDE committed Dec 8, 2023
1 parent 2052151 commit afa84ce
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 9 deletions.
28 changes: 28 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"log"
"math/rand"
"os"
"sync"
"time"
Expand All @@ -27,6 +28,7 @@ var loadConfigOnce sync.Once
type Server struct {
Addr string
MediaPool string
Groups []string
Fallbacks []string

dynamic bool
Expand Down Expand Up @@ -236,6 +238,32 @@ func (cnf Config) Pools() map[string]map[string]Server {
return pools
}

// RandomGroupServer returns the name of a random member of a server group
// or the input string if it is a valid, existent server name.
// It also returns a boolean indicating success.
// The returned string is blank if there is a failure,
// i.e. if the input string is neither a server nor a group.
func (cnf Config) RandomGroupServer(search string) (string, bool) {
candidates := make([]string, 0)
for name, srv := range cnf.Servers {
if name == search {
return name, true
}

for _, grp := range srv.Groups {
if grp == search {
candidates = append(candidates, name)
}
}
}

if len(candidates) == 0 {
return "", false
}

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 {
Expand Down
11 changes: 11 additions & 0 deletions doc/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@ See [media_pools.md](https://github.com/HimbeerserverDE/mt-multiserver-proxy/blo
for more information.
```

> `Server.Groups`
```
Type: []string
Default: []string{}
Description: The server groups this server is in. Group hopping and default server
selection accept server groups on top of regular server names,
randomly choosing one of its servers. If a server name and a group name
are in conflict, the server name is preferred.
This feature can be used to implement simple load balancing.
```

> `Server.Fallbacks`
```
Type: []string
Expand Down
20 changes: 19 additions & 1 deletion hop.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ var (
)

// Hop connects the ClientConn to the specified upstream server
// or the first working fallback server, saving the player's last server.
// or the first working fallback server, saving the player's last server
// unless `ForceDefaultSrv` is enabled.
// If all attempts fail the client is kicked.
// At the moment the ClientConn is NOT fixed if an error occurs
// so the player may have to reconnect.
Expand Down Expand Up @@ -49,6 +50,23 @@ func (cc *ClientConn) Hop(serverName string) (err error) {
return nil
}

// HopGroup connects the ClientConn to the specified server group
// or the first working fallback server, saving the player's last server
// unless `ForceDefaultSrv` is enabled.
// See the documentation on `Server.Groups` in `doc/config.md`
// for details on how a specific game server is selected from the group name.
// If all attempts fail the client is kicked.
// At the moment the ClientConn is NOT fixed if an error occurs
// so the player may have to reconnect.
func (cc *ClientConn) HopGroup(groupName string) error {
choice, ok := Conf().RandomGroupServer(groupName)
if !ok {
return ErrNoSuchServer
}

return cc.Hop(choice)
}

// HopRaw connects the ClientConn to the specified upstream server.
// At the moment the ClientConn is NOT fixed if an error occurs
// so the player may have to reconnect.
Expand Down
15 changes: 7 additions & 8 deletions run.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,14 @@ func runFunc() {

srvName, srv := conf.DefaultServerInfo()
lastSrv, err := authIface.LastSrv(cc.Name())
if err == nil && !Conf().ForceDefaultSrv && lastSrv != srvName {
for name, s := range conf.Servers {
if name == lastSrv {
srvName = name
srv = s

break
}
if err == nil && !conf.ForceDefaultSrv && lastSrv != srvName {
choice, ok := conf.RandomGroupServer(lastSrv)
if !ok {
cc.Log("<-", "inexistent previous server")
}

srvName = choice
srv, _ = conf.Servers[choice] // Existence already checked.
}

addr, err := net.ResolveUDPAddr("udp", srv.Addr)
Expand Down

0 comments on commit afa84ce

Please sign in to comment.