From f6212aeeee08cc81fbf4068a14644a4a031acefa Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 09:33:43 +0200 Subject: [PATCH 01/21] Rename DelServer to RmServer Closes #95 --- config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.go b/config.go index 3b4a6ba..1401686 100644 --- a/config.go +++ b/config.go @@ -102,8 +102,8 @@ func AddServer(server Server) bool { return true } -// DelServer removes a server based on name. -func DelServer(name string) bool { +// RmServer removes a server based on name. +func RmServer(name string) bool { configMu.Lock() defer configMu.Unlock() From b91b3cfa507d296f716b0eeb1afa3be4047f0c3d Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 09:43:36 +0200 Subject: [PATCH 02/21] Improve AddServer and RmServer documentation --- config.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/config.go b/config.go index 1401686..4fe9851 100644 --- a/config.go +++ b/config.go @@ -87,7 +87,12 @@ func Conf() Config { return config } -// AddServer appends a server to the list of configured servers. +// AddServer dynamically configures a new Server at runtime. +// Servers added in this way are ephemeral and will be lost +// when the proxy shuts down. +// For the media to work you have to specify an alternative +// media source that is always available, even if the server +// is offline. func AddServer(server Server) bool { configMu.Lock() defer configMu.Unlock() @@ -102,7 +107,9 @@ func AddServer(server Server) bool { return true } -// RmServer removes a server based on name. +// RmServer deletes a Server from the Config at runtime. +// Any server can be deleted this way, not just the ones +// added using AddServer. func RmServer(name string) bool { configMu.Lock() defer configMu.Unlock() From b870182c52a80046d609af11d4b196e9d6e3e56b Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 09:47:55 +0200 Subject: [PATCH 03/21] Make RmServer return nothing The server is guaranteed to not exist and if you need to look that up you should just do it directly --- config.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/config.go b/config.go index 4fe9851..0d42ab2 100644 --- a/config.go +++ b/config.go @@ -110,18 +110,16 @@ func AddServer(server Server) bool { // RmServer deletes a Server from the Config at runtime. // Any server can be deleted this way, not just the ones // added using AddServer. -func RmServer(name string) bool { +func RmServer(name string) { configMu.Lock() defer configMu.Unlock() for i, srv := range config.Servers { if srv.Name == name { config.Servers = append(config.Servers[:i], config.Servers[1+i:]...) - return true + return } } - - return false } // FallbackServers returns a slice of server names that From c71129fdf4827d1757ce6d3d2be7c89e22987560 Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 09:54:44 +0200 Subject: [PATCH 04/21] Warn about config reloading overwriting runtime changes --- config.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config.go b/config.go index 0d42ab2..812c5fd 100644 --- a/config.go +++ b/config.go @@ -93,6 +93,8 @@ func Conf() Config { // For the media to work you have to specify an alternative // media source that is always available, even if the server // is offline. +// WARNING: Reloading the configuration file deletes the +// servers added using this function. func AddServer(server Server) bool { configMu.Lock() defer configMu.Unlock() @@ -110,6 +112,8 @@ func AddServer(server Server) bool { // RmServer deletes a Server from the Config at runtime. // Any server can be deleted this way, not just the ones // added using AddServer. +// WARNING: Reloading the configuration files re-adds the +// servers it contains that were deleted using this function. func RmServer(name string) { configMu.Lock() defer configMu.Unlock() From 01c26985b161c66aa230ea537549eda57a1360ad Mon Sep 17 00:00:00 2001 From: Riley Date: Sun, 1 May 2022 10:14:27 +0200 Subject: [PATCH 05/21] texturePools! --- config.go | 33 +++++++++++++++++++++--- connect.go | 24 ++++++++++++++---- content.go | 68 +++++++++++++++++++++++++++++--------------------- formspec.go | 2 +- process.go | 42 +++++++++++++++++-------------- server_conn.go | 2 ++ 6 files changed, 114 insertions(+), 57 deletions(-) diff --git a/config.go b/config.go index 3b4a6ba..25319ad 100644 --- a/config.go +++ b/config.go @@ -23,9 +23,10 @@ var configMu sync.RWMutex var loadConfigOnce sync.Once type Server struct { - Name string - Addr string - Fallbacks []string + Name string + Addr string + TexturePool string + Fallbacks []string } // A Config contains information from the configuration file @@ -87,6 +88,32 @@ func Conf() Config { return config } +// UniquePoolServers returns a []server where each Pool is only +// represented once +func UniquePoolServers() []Server { + var srvs []Server + conf := Conf() + + for _, srv := range conf.Servers { + if len(srv.TexturePool) == 0 { + srv.TexturePool = srv.Name + } + } + +AddLoop: + for _, srv := range conf.Servers { + for _, srv2 := range srvs { + if srv.TexturePool == srv2.TexturePool { + continue AddLoop + } + } + + srvs = append(srvs, srv) + } + + return srvs +} + // AddServer appends a server to the list of configured servers. func AddServer(server Server) bool { configMu.Lock() diff --git a/connect.go b/connect.go index 78f6184..32b892c 100644 --- a/connect.go +++ b/connect.go @@ -17,13 +17,26 @@ func connect(conn net.Conn, name string, cc *ClientConn) *ServerConn { } cc.mu.RUnlock() - prefix := fmt.Sprintf("[server %s] ", name) + conf := Conf() + var prefix string + for _, srv := range conf.Servers { + if srv.Name == name { + if len(srv.TexturePool) == 0 { + prefix = srv.Name + } else { + prefix = srv.TexturePool + } + } + } + + logPrefix := fmt.Sprintf("[server %s] ", name) sc := &ServerConn{ Peer: mt.Connect(conn), - logger: log.New(logWriter, prefix, log.LstdFlags|log.Lmsgprefix), + logger: log.New(logWriter, logPrefix, log.LstdFlags|log.Lmsgprefix), initCh: make(chan struct{}), clt: cc, name: name, + prefix: prefix, aos: make(map[mt.AOID]struct{}), particleSpawners: make(map[mt.ParticleSpawnerID]struct{}), sounds: make(map[mt.SoundID]struct{}), @@ -40,14 +53,15 @@ func connect(conn net.Conn, name string, cc *ClientConn) *ServerConn { return sc } -func connectContent(conn net.Conn, name, userName string) (*contentConn, error) { - prefix := fmt.Sprintf("[content %s] ", name) +func connectContent(conn net.Conn, name, userName, prefix string) (*contentConn, error) { + logPrefix := fmt.Sprintf("[content %s] ", name) cc := &contentConn{ Peer: mt.Connect(conn), - logger: log.New(logWriter, prefix, log.LstdFlags|log.Lmsgprefix), + logger: log.New(logWriter, logPrefix, log.LstdFlags|log.Lmsgprefix), doneCh: make(chan struct{}), name: name, userName: userName, + prefix: prefix, } if err := cc.addDefaultTextures(); err != nil { diff --git a/content.go b/content.go index ed7a6ce..9696328 100644 --- a/content.go +++ b/content.go @@ -35,6 +35,8 @@ type contentConn struct { logger *log.Logger + prefix string + cstate clientState cstateMu sync.RWMutex name, userName string @@ -357,21 +359,21 @@ func muxItemDefs(conns []*contentConn) ([]mt.ItemDef, []struct{ Alias, Orig stri def.Name = "hand" } - prepend(cc.name, &def.Name) - prependTexture(cc.name, &def.InvImg) - prependTexture(cc.name, &def.WieldImg) - prepend(cc.name, &def.PlacePredict) - prepend(cc.name, &def.PlaceSnd.Name) - prepend(cc.name, &def.PlaceFailSnd.Name) - prependTexture(cc.name, &def.Palette) - prependTexture(cc.name, &def.InvOverlay) - prependTexture(cc.name, &def.WieldOverlay) + prepend(cc.prefix, &def.Name) + prependTexture(cc.prefix, &def.InvImg) + prependTexture(cc.prefix, &def.WieldImg) + prepend(cc.prefix, &def.PlacePredict) + prepend(cc.prefix, &def.PlaceSnd.Name) + prepend(cc.prefix, &def.PlaceFailSnd.Name) + prependTexture(cc.prefix, &def.Palette) + prependTexture(cc.prefix, &def.InvOverlay) + prependTexture(cc.prefix, &def.WieldOverlay) itemDefs = append(itemDefs, def) } for _, alias := range cc.aliases { - prepend(cc.name, &alias.Alias) - prepend(cc.name, &alias.Orig) + prepend(cc.prefix, &alias.Alias) + prepend(cc.prefix, &alias.Orig) aliases = append(aliases, struct{ Alias, Orig string }{ Alias: alias.Alias, @@ -429,25 +431,25 @@ func muxNodeDefs(conns []*contentConn) (nodeDefs []mt.NodeDef, p0Map param0Map, } def.Param0 = param0 - prepend(cc.name, &def.Name) - prepend(cc.name, &def.Mesh) + prepend(cc.prefix, &def.Name) + prepend(cc.prefix, &def.Mesh) for i := range def.Tiles { - prependTexture(cc.name, &def.Tiles[i].Texture) + prependTexture(cc.prefix, &def.Tiles[i].Texture) } for i := range def.OverlayTiles { - prependTexture(cc.name, &def.OverlayTiles[i].Texture) + prependTexture(cc.prefix, &def.OverlayTiles[i].Texture) } for i := range def.SpecialTiles { - prependTexture(cc.name, &def.SpecialTiles[i].Texture) + prependTexture(cc.prefix, &def.SpecialTiles[i].Texture) } - prependTexture(cc.name, &def.Palette) + prependTexture(cc.prefix, &def.Palette) for k, v := range def.ConnectTo { def.ConnectTo[k] = p0Map[cc.name][v] } - prepend(cc.name, &def.FootstepSnd.Name) - prepend(cc.name, &def.DiggingSnd.Name) - prepend(cc.name, &def.DugSnd.Name) - prepend(cc.name, &def.DigPredict) + prepend(cc.prefix, &def.FootstepSnd.Name) + prepend(cc.prefix, &def.DiggingSnd.Name) + prepend(cc.prefix, &def.DugSnd.Name) + prepend(cc.prefix, &def.DigPredict) nodeDefs = append(nodeDefs, def) param0++ @@ -466,7 +468,7 @@ func muxMedia(conns []*contentConn) []mediaFile { for _, cc := range conns { <-cc.done() for _, f := range cc.media { - prepend(cc.name, &f.name) + prepend(cc.prefix, &f.name) media = append(media, f) } } @@ -494,7 +496,7 @@ func muxRemotes(conns []*contentConn) []string { func muxContent(userName string) (itemDefs []mt.ItemDef, aliases []struct{ Alias, Orig string }, nodeDefs []mt.NodeDef, p0Map param0Map, p0SrvMap param0SrvMap, media []mediaFile, remotes []string, err error) { var conns []*contentConn - for _, srv := range Conf().Servers { + for _, srv := range UniquePoolServers() { var addr *net.UDPAddr addr, err = net.ResolveUDPAddr("udp", srv.Addr) if err != nil { @@ -507,8 +509,16 @@ func muxContent(userName string) (itemDefs []mt.ItemDef, aliases []struct{ Alias return } + // get prefix of server + var prefix string + if len(srv.TexturePool) == 0 { + prefix = srv.Name + } else { + prefix = srv.TexturePool + } + var cc *contentConn - cc, err = connectContent(conn, srv.Name, userName) + cc, err = connectContent(conn, srv.Name, userName, prefix) if err != nil { return } @@ -594,7 +604,7 @@ func prependTexture(prep string, t *mt.Texture) { 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) + prepend(sc.prefix, &inv[k].InvList.Stacks[i].Name) } } } @@ -603,28 +613,28 @@ func (sc *ServerConn) prependHUD(t mt.HUDType, cmdIface mt.ToCltCmd) { pa := func(cmd *mt.ToCltAddHUD) { switch t { case mt.StatbarHUD: - prepend(sc.name, &cmd.Text2) + prepend(sc.prefix, &cmd.Text2) fallthrough case mt.ImgHUD: fallthrough case mt.ImgWaypointHUD: fallthrough case mt.ImgWaypointHUD + 1: - prepend(sc.name, &cmd.Text) + prepend(sc.prefix, &cmd.Text) } } pc := func(cmd *mt.ToCltChangeHUD) { switch t { case mt.StatbarHUD: - prepend(sc.name, &cmd.Text2) + prepend(sc.prefix, &cmd.Text2) fallthrough case mt.ImgHUD: fallthrough case mt.ImgWaypointHUD: fallthrough case mt.ImgWaypointHUD + 1: - prepend(sc.name, &cmd.Text) + prepend(sc.prefix, &cmd.Text) } } diff --git a/formspec.go b/formspec.go index a8a1ea2..169d2f8 100644 --- a/formspec.go +++ b/formspec.go @@ -13,7 +13,7 @@ func (sc *ServerConn) prependFormspec(fs *string) { for i, sub := range subs { if textureName.MatchString(sub) && !strings.Contains(sub, " ") { - prepend(sc.name, &subs[i]) + prepend(sc.prefix, &subs[i]) } } diff --git a/process.go b/process.go index 5208c97..9b46f05 100644 --- a/process.go +++ b/process.go @@ -13,6 +13,15 @@ import ( func (cc *ClientConn) process(pkt mt.Pkt) { srv := cc.server() + forward := func(pkt mt.Pkt) { + if srv == nil { + cc.Log("->", "no server") + return + } + + srv.Send(pkt) + } + switch cmd := pkt.Cmd.(type) { case *mt.ToSrvNil: return @@ -442,12 +451,7 @@ func (cc *ClientConn) process(pkt mt.Pkt) { return } - if srv == nil { - cc.Log("->", "no server") - return - } - - srv.Send(pkt) + forward(pkt) } func (sc *ServerConn) process(pkt mt.Pkt) { @@ -610,7 +614,7 @@ func (sc *ServerConn) process(pkt mt.Pkt) { handStack := mt.Stack{ Item: mt.Item{ - Name: sc.name + "_hand", + Name: sc.prefix + "_hand", }, Count: 1, } @@ -728,7 +732,7 @@ func (sc *ServerConn) process(pkt mt.Pkt) { break } - prepend(sc.name, &cmd.Filename) + prepend(sc.prefix, &cmd.Filename) if cmd.ShouldCache { cacheMedia(mediaFile{ name: cmd.Filename, @@ -738,17 +742,17 @@ func (sc *ServerConn) process(pkt mt.Pkt) { } case *mt.ToCltSkyParams: for i := range cmd.Textures { - prependTexture(sc.name, &cmd.Textures[i]) + prependTexture(sc.prefix, &cmd.Textures[i]) } case *mt.ToCltSunParams: - prependTexture(sc.name, &cmd.Texture) - prependTexture(sc.name, &cmd.ToneMap) - prependTexture(sc.name, &cmd.Rise) + prependTexture(sc.prefix, &cmd.Texture) + prependTexture(sc.prefix, &cmd.ToneMap) + prependTexture(sc.prefix, &cmd.Rise) case *mt.ToCltMoonParams: - prependTexture(sc.name, &cmd.Texture) - prependTexture(sc.name, &cmd.ToneMap) + prependTexture(sc.prefix, &cmd.Texture) + prependTexture(sc.prefix, &cmd.ToneMap) case *mt.ToCltSetHotbarParam: - prependTexture(sc.name, &cmd.Img) + prependTexture(sc.prefix, &cmd.Img) case *mt.ToCltUpdatePlayerList: if !clt.playerListInit { clt.playerListInit = true @@ -766,7 +770,7 @@ func (sc *ServerConn) process(pkt mt.Pkt) { } } case *mt.ToCltSpawnParticle: - prependTexture(sc.name, &cmd.Texture) + prependTexture(sc.prefix, &cmd.Texture) sc.globalParam0(&cmd.NodeParam0) case *mt.ToCltBlkData: for i := range cmd.Blk.Param0 { @@ -785,14 +789,14 @@ func (sc *ServerConn) process(pkt mt.Pkt) { case *mt.ToCltAddNode: sc.globalParam0(&cmd.Node.Param0) case *mt.ToCltAddParticleSpawner: - prependTexture(sc.name, &cmd.Texture) + prependTexture(sc.prefix, &cmd.Texture) sc.swapAOID(&cmd.AttachedAOID) sc.globalParam0(&cmd.NodeParam0) sc.particleSpawners[cmd.ID] = struct{}{} case *mt.ToCltDelParticleSpawner: delete(sc.particleSpawners, cmd.ID) case *mt.ToCltPlaySound: - prepend(sc.name, &cmd.Name) + prepend(sc.prefix, &cmd.Name) sc.swapAOID(&cmd.SrcAOID) if cmd.Loop { sc.sounds[cmd.ID] = struct{}{} @@ -817,7 +821,7 @@ func (sc *ServerConn) process(pkt mt.Pkt) { sc.prependFormspec(&cmd.Formspec) case *mt.ToCltMinimapModes: for i := range cmd.Modes { - prependTexture(sc.name, &cmd.Modes[i].Texture) + prependTexture(sc.prefix, &cmd.Modes[i].Texture) } case *mt.ToCltNodeMetasChanged: for k := range cmd.Changed { diff --git a/server_conn.go b/server_conn.go index 53c7f1a..10ca868 100644 --- a/server_conn.go +++ b/server_conn.go @@ -17,6 +17,8 @@ type ServerConn struct { clt *ClientConn mu sync.RWMutex + prefix string + logger *log.Logger cstate clientState From d0a54602488853a517122dfc2bb7a184f716e6db Mon Sep 17 00:00:00 2001 From: Riley Date: Sun, 1 May 2022 10:16:58 +0200 Subject: [PATCH 06/21] texturePools documentation --- doc/config.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/doc/config.md b/doc/config.md index a7d9ba3..76aae2b 100644 --- a/doc/config.md +++ b/doc/config.md @@ -13,11 +13,13 @@ This is an example configuration file with two servers. Remember to install "Servers": [ { "Name": "default_server", - "Addr": "minetest.local:30000" + "Addr": "minetest.local:30000", + "TexturePool": "mt_game" }, { "Name": "some_other_server", - "Addr": "minetest.local:30001" + "Addr": "minetest.local:30001", + "TexturePool": "mt_game" } ] } @@ -117,6 +119,13 @@ Default: "" Description: The network address and port of an internal server. ``` +> `Server.TexturePool` +``` +Type: string +Default: "" +Description: The texture Pool the server will be mapped to default is server name. +``` + > `Server.Fallback` ``` Type: []string From fabb8e2400e22a638c105c0a9cd99919f2fc7dee Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 10:28:41 +0200 Subject: [PATCH 07/21] Make chat command handlers run concurrently They can't hang the packet handler anymore and the user is warned if commands take a lot of time to run --- chat.go | 4 ++++ process.go | 33 +++++++++++++++++++++++++++------ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/chat.go b/chat.go index 61e8ca8..6257c8b 100644 --- a/chat.go +++ b/chat.go @@ -9,6 +9,10 @@ import ( "github.com/anon55555/mt" ) +// ChatCmdTimeout is the time needed until a user is warned +// about a chat command that's taking long to execute. +var ChatCmdTimeout = 10 * time.Second + // SendChatMsg sends a chat message to the ClientConn. func (cc *ClientConn) SendChatMsg(msg ...string) { cc.SendCmd(&mt.ToCltChatMsg{ diff --git a/process.go b/process.go index 5208c97..3fe0c24 100644 --- a/process.go +++ b/process.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "strings" + "time" "github.com/HimbeerserverDE/srp" "github.com/anon55555/mt" @@ -432,12 +433,32 @@ func (cc *ClientConn) process(pkt mt.Pkt) { srv.swapAOID(&cmd.Pointed.(*mt.PointedAO).ID) } case *mt.ToSrvChatMsg: - result, isCmd := onChatMsg(cc, cmd) - if !isCmd { - break - } else if result != "" { - cc.SendChatMsg(result) - } + done := make(chan struct{}) + + go func(done chan<- struct{}) { + result, isCmd := onChatMsg(cc, cmd) + if !isCmd { + if srv == nil { + cc.Log("->", "no server") + return + } + + srv.Send(pkt) + } else if result != "" { + cc.SendChatMsg(result) + } + + close(done) + }(done) + + go func(done <-chan struct{}) { + select { + case <-done: + case <-time.After(ChatCmdTimeout): + cmdName := strings.Split(cmd.Msg, " ")[0] + cc.SendChatMsg("Command", cmdName, "is taking suspiciously long to execute.") + } + }(done) return } From c259048067c3516f5e40acb8fb8983b64b1274b8 Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 10:30:16 +0200 Subject: [PATCH 08/21] Correct version string --- proxy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy.go b/proxy.go index 1375e1d..3d2f69d 100644 --- a/proxy.go +++ b/proxy.go @@ -16,7 +16,7 @@ import ( const ( latestSerializeVer = 28 latestProtoVer = 39 - versionString = "5.5.0-dev-83a7b48bb" + versionString = "5.4.1" maxPlayerNameLen = 20 bytesPerMediaBunch = 5000 ) From 5f96314dd428428269fc1df821561aad9934038d Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 15:37:05 +0200 Subject: [PATCH 09/21] Refuse to RmServer if players are connected to it --- config.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/config.go b/config.go index 812c5fd..93ee3fa 100644 --- a/config.go +++ b/config.go @@ -112,18 +112,28 @@ func AddServer(server Server) bool { // RmServer deletes a Server from the Config at runtime. // Any server can be deleted this way, not just the ones // added using AddServer. +// Returns true on success or if the server doesn't exist. // WARNING: Reloading the configuration files re-adds the // servers it contains that were deleted using this function. -func RmServer(name string) { +func RmServer(name string) bool { configMu.Lock() defer configMu.Unlock() for i, srv := range config.Servers { if srv.Name == name { + // Can't remove server if players are connected to it + for cc := range Clts() { + if cc.ServerName() == name { + return false + } + } + config.Servers = append(config.Servers[:i], config.Servers[1+i:]...) - return + return true } } + + return true } // FallbackServers returns a slice of server names that From 08771a178415ede970eb9648bd85f528d373265d Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 16:14:16 +0200 Subject: [PATCH 10/21] More logical handling of dynamic servers and config reloading Fixes #97 --- config.go | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/config.go b/config.go index 93ee3fa..60fa1e5 100644 --- a/config.go +++ b/config.go @@ -2,6 +2,7 @@ package proxy import ( "encoding/json" + "fmt" "log" "os" "sync" @@ -26,6 +27,7 @@ type Server struct { Name string Addr string Fallbacks []string + dynamic bool } // A Config contains information from the configuration file @@ -93,34 +95,34 @@ func Conf() Config { // For the media to work you have to specify an alternative // media source that is always available, even if the server // is offline. -// WARNING: Reloading the configuration file deletes the -// servers added using this function. -func AddServer(server Server) bool { +func AddServer(s Server) bool { configMu.Lock() defer configMu.Unlock() + s.dynamic = true for _, srv := range config.Servers { - if srv.Name == server.Name { + if srv.Name == s.Name { return false } } - config.Servers = append(config.Servers, server) + config.Servers = append(config.Servers, s) return true } // RmServer deletes a Server from the Config at runtime. -// Any server can be deleted this way, not just the ones -// added using AddServer. +// Only servers added using AddServer can be deleted at runtime. // Returns true on success or if the server doesn't exist. -// WARNING: Reloading the configuration files re-adds the -// servers it contains that were deleted using this function. func RmServer(name string) bool { configMu.Lock() defer configMu.Unlock() for i, srv := range config.Servers { if srv.Name == name { + if srv.dynamic { + return false + } + // Can't remove server if players are connected to it for cc := range Clts() { if cc.ServerName() == name { @@ -204,6 +206,36 @@ func LoadConfig() error { return err } + // Dynamic servers shouldn't be deleted silently. +DynLoop: + for _, srv := range oldConf.Servers { + if srv.dynamic { + config.Servers = append(config.Servers, srv) + } else { + for _, s := range config.Servers { + if srv.Name == s.Name { + continue DynLoop + } + } + + for cc := range Clts() { + if cc.ServerName() == srv.Name { + config = oldConf + return fmt.Errorf("can't delete server %s with players", srv.Name) + } + } + } + } + + for _, srv := range config.Servers { + for _, s := range config.Servers { + if srv.Name == s.Name { + config = oldConf + return fmt.Errorf("duplicate server %s", s.Name) + } + } + } + log.Print("load config") return nil } From b8fb482ff925aa004527cf246eb985a9d0017d58 Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 16:33:37 +0200 Subject: [PATCH 11/21] Fix UniquePoolServers behavior and quality --- config.go | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/config.go b/config.go index 8ae75d9..cfcf9ee 100644 --- a/config.go +++ b/config.go @@ -24,10 +24,10 @@ var configMu sync.RWMutex var loadConfigOnce sync.Once type Server struct { - Name string - Addr string + Name string + Addr string TexturePool string - Fallbacks []string + Fallbacks []string dynamic bool } @@ -91,23 +91,17 @@ func Conf() Config { return config } -// UniquePoolServers returns a []server where each Pool is only -// represented once +// UniquePoolServers returns a []Server where each +// TexturePool is only represented once. func UniquePoolServers() []Server { var srvs []Server conf := Conf() +AppendLoop: for _, srv := range conf.Servers { - if len(srv.TexturePool) == 0 { - srv.TexturePool = srv.Name - } - } - -AddLoop: - for _, srv := range conf.Servers { - for _, srv2 := range srvs { - if srv.TexturePool == srv2.TexturePool { - continue AddLoop + for _, s := range srvs { + if srv.TexturePool == s.TexturePool { + continue AppendLoop } } @@ -255,13 +249,17 @@ DynLoop: } } - for _, srv := range config.Servers { + for i, srv := range config.Servers { for _, s := range config.Servers { if srv.Name == s.Name { config = oldConf return fmt.Errorf("duplicate server %s", s.Name) } } + + if srv.TexturePool == "" { + config.Servers[i].TexturePool = srv.Name + } } log.Print("load config") From c886245d8e258078089c3d511e5181b4a9216710 Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 16:58:11 +0200 Subject: [PATCH 12/21] Media pool tidying --- connect.go | 27 ++++++++------------ content.go | 68 ++++++++++++++++++++++---------------------------- server_conn.go | 4 +-- 3 files changed, 43 insertions(+), 56 deletions(-) diff --git a/connect.go b/connect.go index 32b892c..714ce79 100644 --- a/connect.go +++ b/connect.go @@ -17,15 +17,10 @@ func connect(conn net.Conn, name string, cc *ClientConn) *ServerConn { } cc.mu.RUnlock() - conf := Conf() - var prefix string - for _, srv := range conf.Servers { + var mediaPool string + for _, srv := range Conf().Servers { if srv.Name == name { - if len(srv.TexturePool) == 0 { - prefix = srv.Name - } else { - prefix = srv.TexturePool - } + mediaPool = srv.TexturePool } } @@ -36,7 +31,7 @@ func connect(conn net.Conn, name string, cc *ClientConn) *ServerConn { initCh: make(chan struct{}), clt: cc, name: name, - prefix: prefix, + mediaPool: mediaPool, aos: make(map[mt.AOID]struct{}), particleSpawners: make(map[mt.ParticleSpawnerID]struct{}), sounds: make(map[mt.SoundID]struct{}), @@ -53,15 +48,15 @@ func connect(conn net.Conn, name string, cc *ClientConn) *ServerConn { return sc } -func connectContent(conn net.Conn, name, userName, prefix string) (*contentConn, error) { +func connectContent(conn net.Conn, name, userName, mediaPool string) (*contentConn, error) { logPrefix := fmt.Sprintf("[content %s] ", name) cc := &contentConn{ - Peer: mt.Connect(conn), - logger: log.New(logWriter, logPrefix, log.LstdFlags|log.Lmsgprefix), - doneCh: make(chan struct{}), - name: name, - userName: userName, - prefix: prefix, + Peer: mt.Connect(conn), + logger: log.New(logWriter, logPrefix, log.LstdFlags|log.Lmsgprefix), + doneCh: make(chan struct{}), + name: name, + userName: userName, + mediaPool: mediaPool, } if err := cc.addDefaultTextures(); err != nil { diff --git a/content.go b/content.go index 9696328..601c031 100644 --- a/content.go +++ b/content.go @@ -35,8 +35,6 @@ type contentConn struct { logger *log.Logger - prefix string - cstate clientState cstateMu sync.RWMutex name, userName string @@ -47,6 +45,8 @@ type contentConn struct { salt, srpA, a, srpK []byte } + mediaPool string + itemDefs []mt.ItemDef aliases []struct{ Alias, Orig string } @@ -359,21 +359,21 @@ func muxItemDefs(conns []*contentConn) ([]mt.ItemDef, []struct{ Alias, Orig stri def.Name = "hand" } - prepend(cc.prefix, &def.Name) - prependTexture(cc.prefix, &def.InvImg) - prependTexture(cc.prefix, &def.WieldImg) - prepend(cc.prefix, &def.PlacePredict) - prepend(cc.prefix, &def.PlaceSnd.Name) - prepend(cc.prefix, &def.PlaceFailSnd.Name) - prependTexture(cc.prefix, &def.Palette) - prependTexture(cc.prefix, &def.InvOverlay) - prependTexture(cc.prefix, &def.WieldOverlay) + prepend(cc.mediaPool, &def.Name) + prependTexture(cc.mediaPool, &def.InvImg) + prependTexture(cc.mediaPool, &def.WieldImg) + prepend(cc.mediaPool, &def.PlacePredict) + prepend(cc.mediaPool, &def.PlaceSnd.Name) + prepend(cc.mediaPool, &def.PlaceFailSnd.Name) + prependTexture(cc.mediaPool, &def.Palette) + prependTexture(cc.mediaPool, &def.InvOverlay) + prependTexture(cc.mediaPool, &def.WieldOverlay) itemDefs = append(itemDefs, def) } for _, alias := range cc.aliases { - prepend(cc.prefix, &alias.Alias) - prepend(cc.prefix, &alias.Orig) + prepend(cc.mediaPool, &alias.Alias) + prepend(cc.mediaPool, &alias.Orig) aliases = append(aliases, struct{ Alias, Orig string }{ Alias: alias.Alias, @@ -431,25 +431,25 @@ func muxNodeDefs(conns []*contentConn) (nodeDefs []mt.NodeDef, p0Map param0Map, } def.Param0 = param0 - prepend(cc.prefix, &def.Name) - prepend(cc.prefix, &def.Mesh) + prepend(cc.mediaPool, &def.Name) + prepend(cc.mediaPool, &def.Mesh) for i := range def.Tiles { - prependTexture(cc.prefix, &def.Tiles[i].Texture) + prependTexture(cc.mediaPool, &def.Tiles[i].Texture) } for i := range def.OverlayTiles { - prependTexture(cc.prefix, &def.OverlayTiles[i].Texture) + prependTexture(cc.mediaPool, &def.OverlayTiles[i].Texture) } for i := range def.SpecialTiles { - prependTexture(cc.prefix, &def.SpecialTiles[i].Texture) + prependTexture(cc.mediaPool, &def.SpecialTiles[i].Texture) } - prependTexture(cc.prefix, &def.Palette) + prependTexture(cc.mediaPool, &def.Palette) for k, v := range def.ConnectTo { def.ConnectTo[k] = p0Map[cc.name][v] } - prepend(cc.prefix, &def.FootstepSnd.Name) - prepend(cc.prefix, &def.DiggingSnd.Name) - prepend(cc.prefix, &def.DugSnd.Name) - prepend(cc.prefix, &def.DigPredict) + prepend(cc.mediaPool, &def.FootstepSnd.Name) + prepend(cc.mediaPool, &def.DiggingSnd.Name) + prepend(cc.mediaPool, &def.DugSnd.Name) + prepend(cc.mediaPool, &def.DigPredict) nodeDefs = append(nodeDefs, def) param0++ @@ -468,7 +468,7 @@ func muxMedia(conns []*contentConn) []mediaFile { for _, cc := range conns { <-cc.done() for _, f := range cc.media { - prepend(cc.prefix, &f.name) + prepend(cc.mediaPool, &f.name) media = append(media, f) } } @@ -509,16 +509,8 @@ func muxContent(userName string) (itemDefs []mt.ItemDef, aliases []struct{ Alias return } - // get prefix of server - var prefix string - if len(srv.TexturePool) == 0 { - prefix = srv.Name - } else { - prefix = srv.TexturePool - } - var cc *contentConn - cc, err = connectContent(conn, srv.Name, userName, prefix) + cc, err = connectContent(conn, srv.Name, userName, srv.TexturePool) if err != nil { return } @@ -604,7 +596,7 @@ func prependTexture(prep string, t *mt.Texture) { func (sc *ServerConn) prependInv(inv mt.Inv) { for k, l := range inv { for i := range l.Stacks { - prepend(sc.prefix, &inv[k].InvList.Stacks[i].Name) + prepend(sc.mediaPool, &inv[k].InvList.Stacks[i].Name) } } } @@ -613,28 +605,28 @@ func (sc *ServerConn) prependHUD(t mt.HUDType, cmdIface mt.ToCltCmd) { pa := func(cmd *mt.ToCltAddHUD) { switch t { case mt.StatbarHUD: - prepend(sc.prefix, &cmd.Text2) + prepend(sc.mediaPool, &cmd.Text2) fallthrough case mt.ImgHUD: fallthrough case mt.ImgWaypointHUD: fallthrough case mt.ImgWaypointHUD + 1: - prepend(sc.prefix, &cmd.Text) + prepend(sc.mediaPool, &cmd.Text) } } pc := func(cmd *mt.ToCltChangeHUD) { switch t { case mt.StatbarHUD: - prepend(sc.prefix, &cmd.Text2) + prepend(sc.mediaPool, &cmd.Text2) fallthrough case mt.ImgHUD: fallthrough case mt.ImgWaypointHUD: fallthrough case mt.ImgWaypointHUD + 1: - prepend(sc.prefix, &cmd.Text) + prepend(sc.mediaPool, &cmd.Text) } } diff --git a/server_conn.go b/server_conn.go index 10ca868..ba229d1 100644 --- a/server_conn.go +++ b/server_conn.go @@ -17,8 +17,6 @@ type ServerConn struct { clt *ClientConn mu sync.RWMutex - prefix string - logger *log.Logger cstate clientState @@ -31,6 +29,8 @@ type ServerConn struct { salt, srpA, a, srpK []byte } + mediaPool string + inv mt.Inv detachedInvs []string From 7719e4c65aa6f4e0deaca98b9d10faf23c399e28 Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 16:58:57 +0200 Subject: [PATCH 13/21] Remove TexturePool as it's unneeded for a minimal configuration file --- doc/config.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/config.md b/doc/config.md index 76aae2b..f362634 100644 --- a/doc/config.md +++ b/doc/config.md @@ -13,13 +13,11 @@ This is an example configuration file with two servers. Remember to install "Servers": [ { "Name": "default_server", - "Addr": "minetest.local:30000", - "TexturePool": "mt_game" + "Addr": "minetest.local:30000" }, { "Name": "some_other_server", - "Addr": "minetest.local:30001", - "TexturePool": "mt_game" + "Addr": "minetest.local:30001" } ] } From 8ba3bec386a6213e7ac750c33da2c207ea1ef43c Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 17:00:33 +0200 Subject: [PATCH 14/21] Slightly improve Server.TexturePool documentation --- doc/config.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/config.md b/doc/config.md index f362634..7bc0b5f 100644 --- a/doc/config.md +++ b/doc/config.md @@ -120,8 +120,8 @@ Description: The network address and port of an internal server. > `Server.TexturePool` ``` Type: string -Default: "" -Description: The texture Pool the server will be mapped to default is server name. +Default: Server.Name +Description: The texture pool the server will be mapped to. ``` > `Server.Fallback` From 95ead432972ef8be5a451698daf464d2eb77f0e7 Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 17:02:50 +0200 Subject: [PATCH 15/21] More sc.prefix => sc.mediaPool replacement --- formspec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/formspec.go b/formspec.go index 169d2f8..3ffa7ac 100644 --- a/formspec.go +++ b/formspec.go @@ -13,7 +13,7 @@ func (sc *ServerConn) prependFormspec(fs *string) { for i, sub := range subs { if textureName.MatchString(sub) && !strings.Contains(sub, " ") { - prepend(sc.prefix, &subs[i]) + prepend(sc.mediaPool, &subs[i]) } } From 34d33d51ac1f9bd6ea501aae90f8f134f4e03e84 Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 17:05:06 +0200 Subject: [PATCH 16/21] Prepare process.go for merge --- process.go | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/process.go b/process.go index 6737af1..c78daa3 100644 --- a/process.go +++ b/process.go @@ -447,12 +447,7 @@ func (cc *ClientConn) process(pkt mt.Pkt) { go func(done chan<- struct{}) { result, isCmd := onChatMsg(cc, cmd) if !isCmd { - if srv == nil { - cc.Log("->", "no server") - return - } - - srv.Send(pkt) + forward(pkt) } else if result != "" { cc.SendChatMsg(result) } @@ -635,7 +630,7 @@ func (sc *ServerConn) process(pkt mt.Pkt) { handStack := mt.Stack{ Item: mt.Item{ - Name: sc.prefix + "_hand", + Name: sc.mediaPool + "_hand", }, Count: 1, } @@ -753,7 +748,7 @@ func (sc *ServerConn) process(pkt mt.Pkt) { break } - prepend(sc.prefix, &cmd.Filename) + prepend(sc.mediaPool, &cmd.Filename) if cmd.ShouldCache { cacheMedia(mediaFile{ name: cmd.Filename, @@ -763,17 +758,17 @@ func (sc *ServerConn) process(pkt mt.Pkt) { } case *mt.ToCltSkyParams: for i := range cmd.Textures { - prependTexture(sc.prefix, &cmd.Textures[i]) + prependTexture(sc.mediaPool, &cmd.Textures[i]) } case *mt.ToCltSunParams: - prependTexture(sc.prefix, &cmd.Texture) - prependTexture(sc.prefix, &cmd.ToneMap) - prependTexture(sc.prefix, &cmd.Rise) + prependTexture(sc.mediaPool, &cmd.Texture) + prependTexture(sc.mediaPool, &cmd.ToneMap) + prependTexture(sc.mediaPool, &cmd.Rise) case *mt.ToCltMoonParams: - prependTexture(sc.prefix, &cmd.Texture) - prependTexture(sc.prefix, &cmd.ToneMap) + prependTexture(sc.mediaPool, &cmd.Texture) + prependTexture(sc.mediaPool, &cmd.ToneMap) case *mt.ToCltSetHotbarParam: - prependTexture(sc.prefix, &cmd.Img) + prependTexture(sc.mediaPool, &cmd.Img) case *mt.ToCltUpdatePlayerList: if !clt.playerListInit { clt.playerListInit = true @@ -791,7 +786,7 @@ func (sc *ServerConn) process(pkt mt.Pkt) { } } case *mt.ToCltSpawnParticle: - prependTexture(sc.prefix, &cmd.Texture) + prependTexture(sc.mediaPool, &cmd.Texture) sc.globalParam0(&cmd.NodeParam0) case *mt.ToCltBlkData: for i := range cmd.Blk.Param0 { @@ -810,14 +805,14 @@ func (sc *ServerConn) process(pkt mt.Pkt) { case *mt.ToCltAddNode: sc.globalParam0(&cmd.Node.Param0) case *mt.ToCltAddParticleSpawner: - prependTexture(sc.prefix, &cmd.Texture) + prependTexture(sc.mediaPool, &cmd.Texture) sc.swapAOID(&cmd.AttachedAOID) sc.globalParam0(&cmd.NodeParam0) sc.particleSpawners[cmd.ID] = struct{}{} case *mt.ToCltDelParticleSpawner: delete(sc.particleSpawners, cmd.ID) case *mt.ToCltPlaySound: - prepend(sc.prefix, &cmd.Name) + prepend(sc.mediaPool, &cmd.Name) sc.swapAOID(&cmd.SrcAOID) if cmd.Loop { sc.sounds[cmd.ID] = struct{}{} @@ -842,7 +837,7 @@ func (sc *ServerConn) process(pkt mt.Pkt) { sc.prependFormspec(&cmd.Formspec) case *mt.ToCltMinimapModes: for i := range cmd.Modes { - prependTexture(sc.prefix, &cmd.Modes[i].Texture) + prependTexture(sc.mediaPool, &cmd.Modes[i].Texture) } case *mt.ToCltNodeMetasChanged: for k := range cmd.Changed { From d537e252b2faec752e725689f6d8bcf8affaefce Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 17:22:24 +0200 Subject: [PATCH 17/21] Fix inconsistent use of TexturePool and MediaPool --- config.go | 16 ++++++++-------- connect.go | 2 +- content.go | 2 +- doc/config.md | 6 ++++-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/config.go b/config.go index cfcf9ee..c119999 100644 --- a/config.go +++ b/config.go @@ -24,10 +24,10 @@ var configMu sync.RWMutex var loadConfigOnce sync.Once type Server struct { - Name string - Addr string - TexturePool string - Fallbacks []string + Name string + Addr string + MediaPool string + Fallbacks []string dynamic bool } @@ -92,7 +92,7 @@ func Conf() Config { } // UniquePoolServers returns a []Server where each -// TexturePool is only represented once. +// MediaPool is only represented once. func UniquePoolServers() []Server { var srvs []Server conf := Conf() @@ -100,7 +100,7 @@ func UniquePoolServers() []Server { AppendLoop: for _, srv := range conf.Servers { for _, s := range srvs { - if srv.TexturePool == s.TexturePool { + if srv.MediaPool == s.MediaPool { continue AppendLoop } } @@ -257,8 +257,8 @@ DynLoop: } } - if srv.TexturePool == "" { - config.Servers[i].TexturePool = srv.Name + if srv.MediaPool == "" { + config.Servers[i].MediaPool = srv.Name } } diff --git a/connect.go b/connect.go index 714ce79..746caa7 100644 --- a/connect.go +++ b/connect.go @@ -20,7 +20,7 @@ func connect(conn net.Conn, name string, cc *ClientConn) *ServerConn { var mediaPool string for _, srv := range Conf().Servers { if srv.Name == name { - mediaPool = srv.TexturePool + mediaPool = srv.MediaPool } } diff --git a/content.go b/content.go index 601c031..612193e 100644 --- a/content.go +++ b/content.go @@ -510,7 +510,7 @@ func muxContent(userName string) (itemDefs []mt.ItemDef, aliases []struct{ Alias } var cc *contentConn - cc, err = connectContent(conn, srv.Name, userName, srv.TexturePool) + cc, err = connectContent(conn, srv.Name, userName, srv.MediaPool) if err != nil { return } diff --git a/doc/config.md b/doc/config.md index 7bc0b5f..4e90976 100644 --- a/doc/config.md +++ b/doc/config.md @@ -117,11 +117,13 @@ Default: "" Description: The network address and port of an internal server. ``` -> `Server.TexturePool` +> `Server.MediaPool` ``` Type: string Default: Server.Name -Description: The texture pool the server will be mapped to. +Description: The media pool this server will be part of. +See [media_pools.md](https://github.com/HimbeerserverDE/mt-multiserver-proxy/blob/main/doc/media_pools.md) +for more information. ``` > `Server.Fallback` From 5ba1fb6132a16bc2ed2480c3e6ac5e3e85d95752 Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 18:02:48 +0200 Subject: [PATCH 18/21] Document media pools --- doc/media_pools.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 doc/media_pools.md diff --git a/doc/media_pools.md b/doc/media_pools.md new file mode 100644 index 0000000..d2bf68b --- /dev/null +++ b/doc/media_pools.md @@ -0,0 +1,53 @@ +# Media Pools + +All servers must be part of a media pool. By default the name of the server +is used. + +## Background +When the proxy sends any content-related packets to the client, +it prefixes any content names such as node names or media file names +with the media pool of the current server and an underscore. +The purpose of this is to allow servers to have different media +with the same name and to avoid some other multiplexing issues. + +## When to use media pools? +In general, custom media pools are not required. +There are reasons to use them: +- reducing memory and storage usage on the client +- dynamically adding servers + +### Reducing RAM and disk usage +The client has to store all media it receives in memory and in its cache. +Minetest doesn't do this very efficiently: Identical files with different +names will not share memory, a copy will be made. Even if they did share +memory the references would still consume memory themselves but that would +probably be negligable. + +This may not look like a big issue but it is. Many machines, especially +phones, still only have 4 GB of RAM or even less. It's quite easy to +exceed this limit even with lightweight or basic subgames. This will make +devices that don't have enough memory unable to connect. The game will crash +while downloading media. + +The unnecessarily redundant caching will fill the permanent storage with +unneeded files too. This isn't as big of a problem as the cache isn't +(or at least shouldn't) be required for the engine to work. However +inexperienced players are going to wonder where their disk space is going. + +### Dynamic servers +These are a whole other mess but all you need to know is that they won't work +without media pools. The reason is that connected clients can't get the new +content without reconnecting due to engine restrictions. Media pools are +pushed to the client when it connects. This requires the first server of the +media pool to be reachable. This means you can make a dummy server for the +media and prevent players from connecting to it, or just use a hub server +as the media master. + +## How to use media pools? +Simply specify the name of the media pool you'd like the server to be part of +in the MediaPool field of the server definition. All server you do this for +will be part of the pool. Alternatively you can specify the name of another +server if that server doesn't have a custom media pool set or if it's the same +as its name. This will result in the servers being in a media pool that has +the same name as that server. You can use it to your advantage when creating +and naming dummy servers. From 1b2d0d40f704813835bafebe095e7e31fbc00168 Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 18:19:00 +0200 Subject: [PATCH 19/21] Make AddServer require at least one media pool member --- config.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/config.go b/config.go index c119999..a286386 100644 --- a/config.go +++ b/config.go @@ -114,20 +114,32 @@ AppendLoop: // AddServer dynamically configures a new Server at runtime. // Servers added in this way are ephemeral and will be lost // when the proxy shuts down. -// For the media to work you have to specify an alternative -// media source that is always available, even if the server -// is offline. +// 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. func AddServer(s Server) bool { configMu.Lock() defer configMu.Unlock() s.dynamic = true + for _, srv := range config.Servers { if srv.Name == s.Name { return false } } + var poolMembers bool + for _, srv := range config.Servers { + if srv.MediaPool == s.MediaPool { + poolMembers = true + } + } + + if !poolMembers { + return false + } + config.Servers = append(config.Servers, s) return true } From fbf490c12b79d979bcc8076dde9a332926ad3642 Mon Sep 17 00:00:00 2001 From: Riley Date: Sun, 1 May 2022 20:52:17 +0200 Subject: [PATCH 20/21] texturePool things regarding to Issue #101 --- config.go | 28 ++++++++++++++----------- content.go | 61 +++++++++++++++++++++++++++++++----------------------- run.go | 1 + 3 files changed, 52 insertions(+), 38 deletions(-) diff --git a/config.go b/config.go index 16e472c..a57ee4d 100644 --- a/config.go +++ b/config.go @@ -88,30 +88,34 @@ func Conf() Config { return config } -// UniquePoolServers returns a []server where each Pool is only -// represented once -func UniquePoolServers() []Server { - var srvs []Server +// UniquePoolServers returns a [][]server where each Pool is represented by a []Server +// of all servers that use one pool +func UniquePoolServers() [][]Server { + var srvs = make(map[string][]Server) conf := Conf() + // every server needs a texturePool property for _, srv := range conf.Servers { if len(srv.TexturePool) == 0 { srv.TexturePool = srv.Name } } -AddLoop: + // map all to.. map of slices for _, srv := range conf.Servers { - for _, srv2 := range srvs { - if srv.TexturePool == srv2.TexturePool { - continue AddLoop - } + if srvs[srv.TexturePool] != nil { + srvs[srv.TexturePool] = append(srvs[srv.TexturePool], srv) + } else { + srvs[srv.TexturePool] = []Server{srv} } - - srvs = append(srvs, srv) } - return srvs + var res [][]Server + for _, srvsPool := range srvs { + res = append(res, srvsPool) + } + + return res } // AddServer dynamically configures a new Server at runtime. diff --git a/content.go b/content.go index 9696328..2436da7 100644 --- a/content.go +++ b/content.go @@ -496,35 +496,44 @@ func muxRemotes(conns []*contentConn) []string { func muxContent(userName string) (itemDefs []mt.ItemDef, aliases []struct{ Alias, Orig string }, nodeDefs []mt.NodeDef, p0Map param0Map, p0SrvMap param0SrvMap, media []mediaFile, remotes []string, err error) { var conns []*contentConn - for _, srv := range UniquePoolServers() { + for _, pools := range UniquePoolServers() { var addr *net.UDPAddr - addr, err = net.ResolveUDPAddr("udp", srv.Addr) - if err != nil { + found := false + + for _, srv := range pools { + addr, err = net.ResolveUDPAddr("udp", srv.Addr) + if err != nil { + continue + } + + var conn *net.UDPConn + conn, err = net.DialUDP("udp", nil, addr) + if err != nil { + continue + } + + // get prefix of server + var prefix string + if len(srv.TexturePool) == 0 { + prefix = srv.Name + } else { + prefix = srv.TexturePool + } + + var cc *contentConn + cc, err = connectContent(conn, srv.Name, userName, prefix) + if err != nil { + continue + } + defer cc.Close() + + found = true + conns = append(conns, cc) + } + + if !found { return } - - var conn *net.UDPConn - conn, err = net.DialUDP("udp", nil, addr) - if err != nil { - return - } - - // get prefix of server - var prefix string - if len(srv.TexturePool) == 0 { - prefix = srv.Name - } else { - prefix = srv.TexturePool - } - - var cc *contentConn - cc, err = connectContent(conn, srv.Name, userName, prefix) - if err != nil { - return - } - defer cc.Close() - - conns = append(conns, cc) } itemDefs, aliases = muxItemDefs(conns) diff --git a/run.go b/run.go index f7c1d72..4515651 100644 --- a/run.go +++ b/run.go @@ -118,6 +118,7 @@ func runFunc() { } } + cc.Log(srv.Addr) addr, err := net.ResolveUDPAddr("udp", srv.Addr) if err != nil { cc.Log("<-", "address resolution fail") From 74ff3847ea7d92a0d52c782191d9b610ee80292f Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sun, 1 May 2022 22:25:10 +0200 Subject: [PATCH 21/21] Prepare #102 for merge --- config.go | 25 ++++--------------------- content.go | 27 ++++++++++----------------- run.go | 1 - 3 files changed, 14 insertions(+), 39 deletions(-) diff --git a/config.go b/config.go index 95e541d..c3f8dfd 100644 --- a/config.go +++ b/config.go @@ -91,34 +91,17 @@ func Conf() Config { return config } -// UniquePoolServers returns a [][]server where each Pool is represented by a []Server -// of all servers that use one pool -func UniquePoolServers() [][]Server { +// PoolServers returns all media pools and their member servers. +func PoolServers() map[string][]Server { var srvs = make(map[string][]Server) conf := Conf() - // every server needs a texturePool property - for _, srv := range conf.Servers { - if len(srv.MediaPool) == 0 { - srv.MediaPool = srv.Name - } - } - // map all to.. map of slices for _, srv := range conf.Servers { - if srvs[srv.MediaPool] != nil { - srvs[srv.MediaPool] = append(srvs[srv.MediaPool], srv) - } else { - srvs[srv.MediaPool] = []Server{srv} - } + srvs[srv.MediaPool] = append(srvs[srv.MediaPool], srv) } - var res [][]Server - for _, srvsPool := range srvs { - res = append(res, srvsPool) - } - - return res + return srvs } // AddServer dynamically configures a new Server at runtime. diff --git a/content.go b/content.go index 5593bcf..9c1c146 100644 --- a/content.go +++ b/content.go @@ -496,11 +496,12 @@ func muxRemotes(conns []*contentConn) []string { func muxContent(userName string) (itemDefs []mt.ItemDef, aliases []struct{ Alias, Orig string }, nodeDefs []mt.NodeDef, p0Map param0Map, p0SrvMap param0SrvMap, media []mediaFile, remotes []string, err error) { var conns []*contentConn - for _, pools := range UniquePoolServers() { - var addr *net.UDPAddr - found := false - for _, srv := range pools { +PoolLoop: + for _, pool := range PoolServers() { + var addr *net.UDPAddr + + for _, srv := range pool { addr, err = net.ResolveUDPAddr("udp", srv.Addr) if err != nil { continue @@ -512,28 +513,20 @@ func muxContent(userName string) (itemDefs []mt.ItemDef, aliases []struct{ Alias continue } - // get prefix of server - var prefix string - if len(srv.MediaPool) == 0 { - prefix = srv.Name - } else { - prefix = srv.MediaPool - } - var cc *contentConn - cc, err = connectContent(conn, srv.Name, userName, prefix) + cc, err = connectContent(conn, srv.Name, userName, srv.MediaPool) if err != nil { continue } defer cc.Close() - found = true conns = append(conns, cc) + continue PoolLoop } - if !found { - return - } + // There's a pool with no reachable servers. + // We can't safely let clients join. + return } itemDefs, aliases = muxItemDefs(conns) diff --git a/run.go b/run.go index 4515651..f7c1d72 100644 --- a/run.go +++ b/run.go @@ -118,7 +118,6 @@ func runFunc() { } } - cc.Log(srv.Addr) addr, err := net.ResolveUDPAddr("udp", srv.Addr) if err != nil { cc.Log("<-", "address resolution fail")