2021-09-06 02:03:27 -07:00
|
|
|
package proxy
|
2021-08-27 09:59:27 -07:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2022-05-01 07:14:16 -07:00
|
|
|
"fmt"
|
2021-08-27 09:59:27 -07:00
|
|
|
"log"
|
|
|
|
"os"
|
2021-09-05 03:18:22 -07:00
|
|
|
"sync"
|
2021-08-27 09:59:27 -07:00
|
|
|
)
|
|
|
|
|
2021-09-13 03:14:11 -07:00
|
|
|
const (
|
|
|
|
defaultCmdPrefix = ">"
|
|
|
|
defaultSendInterval = 0.09
|
|
|
|
defaultUserLimit = 10
|
2021-09-13 06:54:57 -07:00
|
|
|
defaultAuthBackend = "files"
|
2021-09-13 03:14:11 -07:00
|
|
|
defaultTelnetAddr = "[::1]:40010"
|
|
|
|
defaultBindAddr = ":40000"
|
|
|
|
defaultListInterval = 300
|
|
|
|
)
|
2021-08-27 09:59:27 -07:00
|
|
|
|
2021-09-05 03:18:22 -07:00
|
|
|
var config Config
|
|
|
|
var configMu sync.RWMutex
|
2021-08-27 09:59:27 -07:00
|
|
|
|
2022-04-21 05:35:58 -07:00
|
|
|
var loadConfigOnce sync.Once
|
|
|
|
|
2022-04-21 13:58:04 -07:00
|
|
|
type Server struct {
|
2022-05-01 08:22:24 -07:00
|
|
|
Addr string
|
|
|
|
MediaPool string
|
|
|
|
Fallbacks []string
|
2022-05-01 07:20:20 -07:00
|
|
|
|
|
|
|
dynamic bool
|
2022-04-21 13:58:04 -07:00
|
|
|
}
|
|
|
|
|
2021-09-10 03:47:19 -07:00
|
|
|
// A Config contains information from the configuration file
|
|
|
|
// that affects the way the proxy works.
|
2021-08-27 09:59:27 -07:00
|
|
|
type Config struct {
|
2022-04-21 13:58:04 -07:00
|
|
|
NoPlugins bool
|
|
|
|
CmdPrefix string
|
|
|
|
RequirePasswd bool
|
|
|
|
SendInterval float32
|
|
|
|
UserLimit int
|
|
|
|
AuthBackend string
|
|
|
|
NoTelnet bool
|
|
|
|
TelnetAddr string
|
|
|
|
BindAddr string
|
2022-05-02 12:57:38 -07:00
|
|
|
Servers map[string]Server
|
2021-10-24 06:35:12 -07:00
|
|
|
ForceDefaultSrv bool
|
2022-04-20 13:48:54 -07:00
|
|
|
FallbackServers []string
|
2021-10-24 06:35:12 -07:00
|
|
|
CSMRF struct {
|
2021-08-28 04:05:09 -07:00
|
|
|
NoCSMs bool
|
|
|
|
ChatMsgs bool
|
|
|
|
ItemDefs bool
|
|
|
|
NodeDefs bool
|
2021-08-28 04:02:27 -07:00
|
|
|
NoLimitMapRange bool
|
2021-08-28 04:05:09 -07:00
|
|
|
PlayerList bool
|
2021-08-27 11:40:07 -07:00
|
|
|
}
|
2021-09-07 10:13:12 -07:00
|
|
|
MapRange uint32
|
2021-09-10 07:12:15 -07:00
|
|
|
DropCSMRF bool
|
2021-09-07 10:13:12 -07:00
|
|
|
Groups map[string][]string
|
|
|
|
UserGroups map[string]string
|
2021-09-11 06:38:45 -07:00
|
|
|
List struct {
|
|
|
|
Enable bool
|
|
|
|
Addr string
|
|
|
|
Interval int
|
|
|
|
|
|
|
|
Name string
|
|
|
|
Desc string
|
|
|
|
URL string
|
|
|
|
Creative bool
|
|
|
|
Dmg bool
|
|
|
|
PvP bool
|
|
|
|
Game string
|
|
|
|
FarNames bool
|
|
|
|
Mods []string
|
|
|
|
}
|
2021-08-27 09:59:27 -07:00
|
|
|
}
|
|
|
|
|
2021-09-10 03:47:19 -07:00
|
|
|
// Conf returns a copy of the Config used by the proxy.
|
|
|
|
// Any modifications will not affect the original Config.
|
2021-09-06 02:03:27 -07:00
|
|
|
func Conf() Config {
|
2022-04-21 05:46:13 -07:00
|
|
|
loadConfigOnce.Do(func() {
|
2022-04-21 05:35:58 -07:00
|
|
|
if err := LoadConfig(); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2021-09-05 03:18:22 -07:00
|
|
|
configMu.RLock()
|
|
|
|
defer configMu.RUnlock()
|
|
|
|
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
|
2022-05-01 13:25:10 -07:00
|
|
|
// PoolServers returns all media pools and their member servers.
|
2022-05-02 12:57:38 -07:00
|
|
|
func PoolServers() map[string]map[string]Server {
|
|
|
|
pools := make(map[string]map[string]Server)
|
|
|
|
for name, srv := range Conf().Servers {
|
|
|
|
if pools[srv.MediaPool] == nil {
|
|
|
|
pools[srv.MediaPool] = make(map[string]Server)
|
|
|
|
}
|
2022-05-01 01:14:27 -07:00
|
|
|
|
2022-05-02 12:57:38 -07:00
|
|
|
pools[srv.MediaPool][name] = srv
|
2022-05-01 01:14:27 -07:00
|
|
|
}
|
|
|
|
|
2022-05-02 12:57:38 -07:00
|
|
|
return pools
|
2022-05-01 01:14:27 -07:00
|
|
|
}
|
|
|
|
|
2022-05-01 00:43:36 -07:00
|
|
|
// AddServer dynamically configures a new Server at runtime.
|
|
|
|
// Servers added in this way are ephemeral and will be lost
|
|
|
|
// when the proxy shuts down.
|
2022-05-01 09:19:00 -07:00
|
|
|
// The server must be part of a media pool with at least one
|
|
|
|
// other member. At least one of the other members always
|
|
|
|
// needs to be reachable.
|
2022-05-02 23:03:16 -07:00
|
|
|
// WARNING: Reloading the config will not overwrite servers
|
|
|
|
// added using this function. The server definition from the
|
|
|
|
// configuration file will silently be ignored.
|
2022-05-02 12:57:38 -07:00
|
|
|
func AddServer(name string, s Server) bool {
|
2022-04-21 13:58:04 -07:00
|
|
|
configMu.Lock()
|
|
|
|
defer configMu.Unlock()
|
|
|
|
|
2022-05-01 07:14:16 -07:00
|
|
|
s.dynamic = true
|
2022-05-01 09:19:00 -07:00
|
|
|
|
2022-05-02 12:57:38 -07:00
|
|
|
if _, ok := config.Servers[name]; ok {
|
|
|
|
return false
|
2022-04-21 14:05:18 -07:00
|
|
|
}
|
|
|
|
|
2022-05-01 09:19:00 -07:00
|
|
|
var poolMembers bool
|
|
|
|
for _, srv := range config.Servers {
|
|
|
|
if srv.MediaPool == s.MediaPool {
|
|
|
|
poolMembers = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !poolMembers {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-05-02 12:57:38 -07:00
|
|
|
config.Servers[name] = s
|
2022-04-21 14:05:18 -07:00
|
|
|
return true
|
2022-04-21 13:58:04 -07:00
|
|
|
}
|
|
|
|
|
2022-05-01 00:43:36 -07:00
|
|
|
// RmServer deletes a Server from the Config at runtime.
|
2022-05-01 07:14:16 -07:00
|
|
|
// Only servers added using AddServer can be deleted at runtime.
|
2022-05-01 06:37:05 -07:00
|
|
|
// Returns true on success or if the server doesn't exist.
|
|
|
|
func RmServer(name string) bool {
|
2022-04-21 13:58:04 -07:00
|
|
|
configMu.Lock()
|
|
|
|
defer configMu.Unlock()
|
|
|
|
|
2022-05-02 12:57:38 -07:00
|
|
|
s, ok := config.Servers[name]
|
|
|
|
if !ok {
|
|
|
|
return true
|
|
|
|
}
|
2022-05-01 07:14:16 -07:00
|
|
|
|
2022-05-02 12:57:38 -07:00
|
|
|
if !s.dynamic {
|
|
|
|
return false
|
|
|
|
}
|
2022-05-01 06:37:05 -07:00
|
|
|
|
2022-05-02 12:57:38 -07:00
|
|
|
// Can't remove server if players are connected to it
|
|
|
|
for cc := range Clts() {
|
|
|
|
if cc.ServerName() == name {
|
|
|
|
return false
|
2022-04-21 13:58:04 -07:00
|
|
|
}
|
|
|
|
}
|
2022-05-01 06:37:05 -07:00
|
|
|
|
2022-05-02 12:57:38 -07:00
|
|
|
delete(config.Servers, name)
|
2022-05-01 06:37:05 -07:00
|
|
|
return true
|
2022-04-21 13:58:04 -07:00
|
|
|
}
|
|
|
|
|
2022-05-02 13:03:51 -07:00
|
|
|
// DefaultServerInfo returns both the name of the default server
|
2022-05-02 13:01:18 -07:00
|
|
|
// and information about it. The return values are uninitialized
|
|
|
|
// if no servers exist.
|
2022-05-02 13:03:51 -07:00
|
|
|
func (cnf Config) DefaultServerInfo() (string, Server) {
|
2022-05-02 12:57:38 -07:00
|
|
|
for name, srv := range Conf().Servers {
|
|
|
|
return name, srv
|
|
|
|
}
|
|
|
|
|
|
|
|
// No servers are configured.
|
|
|
|
return "", Server{}
|
|
|
|
}
|
|
|
|
|
2022-05-02 13:03:51 -07:00
|
|
|
// DefaultServerName returns the name of the default server.
|
2022-05-02 12:57:38 -07:00
|
|
|
// If no servers exist it returns an empty string.
|
2022-05-02 13:03:51 -07:00
|
|
|
func (cnf Config) DefaultServerName() string {
|
|
|
|
name, _ := cnf.DefaultServerInfo()
|
2022-05-02 12:57:38 -07:00
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
2022-05-02 13:03:51 -07:00
|
|
|
// DefaultServer returns information about the default server.
|
2022-05-02 12:57:38 -07:00
|
|
|
// If no servers exist the returned struct will be uninitialized.
|
2022-05-02 13:03:51 -07:00
|
|
|
// This is a faster shortcut for Config.Servers[Config.DefaultServerName()].
|
|
|
|
// You should thus only use this method or the DefaultServerInfo method.
|
|
|
|
func (cnf Config) DefaultServer() Server {
|
|
|
|
_, srv := cnf.DefaultServerInfo()
|
2022-05-02 12:57:38 -07:00
|
|
|
return srv
|
|
|
|
}
|
|
|
|
|
2022-04-21 03:31:05 -07:00
|
|
|
// FallbackServers returns a slice of server names that
|
2022-04-20 13:48:54 -07:00
|
|
|
// a server can fall back to.
|
|
|
|
func FallbackServers(server string) []string {
|
2022-04-21 03:35:35 -07:00
|
|
|
conf := Conf()
|
2022-04-21 04:05:01 -07:00
|
|
|
|
2022-05-02 12:57:38 -07:00
|
|
|
srv, ok := conf.Servers[server]
|
|
|
|
if !ok {
|
|
|
|
return nil
|
2022-04-20 13:48:54 -07:00
|
|
|
}
|
|
|
|
|
2022-05-02 12:57:38 -07:00
|
|
|
fallbacks := srv.Fallbacks
|
|
|
|
|
2022-04-20 13:48:54 -07:00
|
|
|
// global fallbacks
|
2022-04-21 03:35:35 -07:00
|
|
|
if len(conf.FallbackServers) == 0 {
|
|
|
|
if len(conf.Servers) == 0 {
|
|
|
|
return fallbacks
|
|
|
|
}
|
2022-04-21 04:05:01 -07:00
|
|
|
|
2022-05-02 13:03:51 -07:00
|
|
|
return append(fallbacks, conf.DefaultServerName())
|
2022-04-20 13:48:54 -07:00
|
|
|
} else {
|
2022-04-21 03:35:35 -07:00
|
|
|
return append(fallbacks, conf.FallbackServers...)
|
2022-04-20 13:48:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-10 03:47:19 -07:00
|
|
|
// LoadConfig attempts to parse the configuration file.
|
|
|
|
// It leaves the config unchanged if there is an error
|
|
|
|
// and returns the error.
|
2021-09-06 02:03:27 -07:00
|
|
|
func LoadConfig() error {
|
2021-09-05 03:18:22 -07:00
|
|
|
configMu.Lock()
|
|
|
|
defer configMu.Unlock()
|
|
|
|
|
|
|
|
oldConf := config
|
2021-08-27 09:59:27 -07:00
|
|
|
|
2021-09-05 10:19:27 -07:00
|
|
|
config.CmdPrefix = defaultCmdPrefix
|
2021-09-05 03:18:22 -07:00
|
|
|
config.SendInterval = defaultSendInterval
|
|
|
|
config.UserLimit = defaultUserLimit
|
|
|
|
config.AuthBackend = defaultAuthBackend
|
2021-09-12 03:03:20 -07:00
|
|
|
config.TelnetAddr = defaultTelnetAddr
|
2021-09-05 03:18:22 -07:00
|
|
|
config.BindAddr = defaultBindAddr
|
2022-04-20 13:48:54 -07:00
|
|
|
config.FallbackServers = make([]string, 0)
|
2021-09-07 10:13:12 -07:00
|
|
|
config.Groups = make(map[string][]string)
|
|
|
|
config.UserGroups = make(map[string]string)
|
2021-09-11 06:55:10 -07:00
|
|
|
config.List.Interval = defaultListInterval
|
2021-08-27 09:59:27 -07:00
|
|
|
|
2021-09-13 06:54:57 -07:00
|
|
|
f, err := os.OpenFile(Path("config.json"), os.O_RDWR|os.O_CREATE, 0666)
|
2021-08-27 09:59:27 -07:00
|
|
|
if err != nil {
|
2021-09-05 03:18:22 -07:00
|
|
|
config = oldConf
|
2021-08-27 09:59:27 -07:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
if fi, _ := f.Stat(); fi.Size() == 0 {
|
2021-08-29 10:45:20 -07:00
|
|
|
f.WriteString("{\n\t\n}\n")
|
2021-08-27 09:59:27 -07:00
|
|
|
f.Seek(0, os.SEEK_SET)
|
|
|
|
}
|
|
|
|
|
|
|
|
decoder := json.NewDecoder(f)
|
2021-09-05 03:18:22 -07:00
|
|
|
if err := decoder.Decode(&config); err != nil {
|
|
|
|
config = oldConf
|
2021-08-27 09:59:27 -07:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-05-01 07:14:16 -07:00
|
|
|
// Dynamic servers shouldn't be deleted silently.
|
2022-05-02 12:57:38 -07:00
|
|
|
for name, srv := range oldConf.Servers {
|
2022-05-01 07:14:16 -07:00
|
|
|
if srv.dynamic {
|
2022-05-02 23:06:07 -07:00
|
|
|
if _, ok := config.Servers[name]; ok {
|
|
|
|
config = oldConf
|
|
|
|
return fmt.Errorf("duplicate server %s", name)
|
|
|
|
}
|
|
|
|
|
2022-05-02 12:57:38 -07:00
|
|
|
config.Servers[name] = srv
|
2022-05-01 07:14:16 -07:00
|
|
|
} else {
|
2022-05-02 23:06:07 -07:00
|
|
|
if _, ok := config.Servers[name]; ok {
|
|
|
|
continue
|
2022-05-01 07:14:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for cc := range Clts() {
|
2022-05-02 12:57:38 -07:00
|
|
|
if cc.ServerName() == name {
|
2022-05-01 07:14:16 -07:00
|
|
|
config = oldConf
|
2022-05-02 12:57:38 -07:00
|
|
|
return fmt.Errorf("can't delete server %s with players", name)
|
2022-05-01 07:14:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-02 12:57:38 -07:00
|
|
|
for name, srv := range config.Servers {
|
2022-05-01 08:22:24 -07:00
|
|
|
if srv.MediaPool == "" {
|
2022-05-02 12:57:38 -07:00
|
|
|
s := config.Servers[name]
|
|
|
|
s.MediaPool = name
|
|
|
|
config.Servers[name] = s
|
2022-05-01 07:33:37 -07:00
|
|
|
}
|
2022-05-01 07:14:16 -07:00
|
|
|
}
|
|
|
|
|
2021-09-13 03:14:11 -07:00
|
|
|
log.Print("load config")
|
2021-08-27 09:59:27 -07:00
|
|
|
return nil
|
|
|
|
}
|