commit
ee77906613
4
chat.go
4
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{
|
||||
|
|
96
config.go
96
config.go
|
@ -2,6 +2,7 @@ package proxy
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
@ -25,7 +26,10 @@ var loadConfigOnce sync.Once
|
|||
type Server struct {
|
||||
Name string
|
||||
Addr string
|
||||
MediaPool string
|
||||
Fallbacks []string
|
||||
|
||||
dynamic bool
|
||||
}
|
||||
|
||||
// A Config contains information from the configuration file
|
||||
|
@ -87,34 +91,78 @@ func Conf() Config {
|
|||
return config
|
||||
}
|
||||
|
||||
// AddServer appends a server to the list of configured servers.
|
||||
func AddServer(server Server) bool {
|
||||
// PoolServers returns all media pools and their member servers.
|
||||
func PoolServers() map[string][]Server {
|
||||
var srvs = make(map[string][]Server)
|
||||
conf := Conf()
|
||||
|
||||
// map all to.. map of slices
|
||||
for _, srv := range conf.Servers {
|
||||
srvs[srv.MediaPool] = append(srvs[srv.MediaPool], srv)
|
||||
}
|
||||
|
||||
return srvs
|
||||
}
|
||||
|
||||
// AddServer dynamically configures a new Server at runtime.
|
||||
// Servers added in this way are ephemeral and will be lost
|
||||
// when the proxy shuts down.
|
||||
// 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 == server.Name {
|
||||
if srv.Name == s.Name {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
config.Servers = append(config.Servers, server)
|
||||
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
|
||||
}
|
||||
|
||||
// DelServer removes a server based on name.
|
||||
func DelServer(name string) bool {
|
||||
// RmServer deletes a Server from the Config at runtime.
|
||||
// Only servers added using AddServer can be deleted at runtime.
|
||||
// Returns true on success or if the server doesn't exist.
|
||||
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 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
config.Servers = append(config.Servers[:i], config.Servers[1+i:]...)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
// FallbackServers returns a slice of server names that
|
||||
|
@ -185,6 +233,40 @@ 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 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.MediaPool == "" {
|
||||
config.Servers[i].MediaPool = srv.Name
|
||||
}
|
||||
}
|
||||
|
||||
log.Print("load config")
|
||||
return nil
|
||||
}
|
||||
|
|
27
connect.go
27
connect.go
|
@ -17,13 +17,21 @@ func connect(conn net.Conn, name string, cc *ClientConn) *ServerConn {
|
|||
}
|
||||
cc.mu.RUnlock()
|
||||
|
||||
prefix := fmt.Sprintf("[server %s] ", name)
|
||||
var mediaPool string
|
||||
for _, srv := range Conf().Servers {
|
||||
if srv.Name == name {
|
||||
mediaPool = srv.MediaPool
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
mediaPool: mediaPool,
|
||||
aos: make(map[mt.AOID]struct{}),
|
||||
particleSpawners: make(map[mt.ParticleSpawnerID]struct{}),
|
||||
sounds: make(map[mt.SoundID]struct{}),
|
||||
|
@ -40,14 +48,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, mediaPool string) (*contentConn, error) {
|
||||
logPrefix := fmt.Sprintf("[content %s] ", name)
|
||||
cc := &contentConn{
|
||||
Peer: mt.Connect(conn),
|
||||
logger: log.New(logWriter, prefix, log.LstdFlags|log.Lmsgprefix),
|
||||
doneCh: make(chan struct{}),
|
||||
name: name,
|
||||
userName: userName,
|
||||
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 {
|
||||
|
|
102
content.go
102
content.go
|
@ -45,6 +45,8 @@ type contentConn struct {
|
|||
salt, srpA, a, srpK []byte
|
||||
}
|
||||
|
||||
mediaPool string
|
||||
|
||||
itemDefs []mt.ItemDef
|
||||
aliases []struct{ Alias, Orig 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.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.name, &alias.Alias)
|
||||
prepend(cc.name, &alias.Orig)
|
||||
prepend(cc.mediaPool, &alias.Alias)
|
||||
prepend(cc.mediaPool, &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.mediaPool, &def.Name)
|
||||
prepend(cc.mediaPool, &def.Mesh)
|
||||
for i := range def.Tiles {
|
||||
prependTexture(cc.name, &def.Tiles[i].Texture)
|
||||
prependTexture(cc.mediaPool, &def.Tiles[i].Texture)
|
||||
}
|
||||
for i := range def.OverlayTiles {
|
||||
prependTexture(cc.name, &def.OverlayTiles[i].Texture)
|
||||
prependTexture(cc.mediaPool, &def.OverlayTiles[i].Texture)
|
||||
}
|
||||
for i := range def.SpecialTiles {
|
||||
prependTexture(cc.name, &def.SpecialTiles[i].Texture)
|
||||
prependTexture(cc.mediaPool, &def.SpecialTiles[i].Texture)
|
||||
}
|
||||
prependTexture(cc.name, &def.Palette)
|
||||
prependTexture(cc.mediaPool, &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.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++
|
||||
|
@ -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.mediaPool, &f.name)
|
||||
media = append(media, f)
|
||||
}
|
||||
}
|
||||
|
@ -494,27 +496,37 @@ 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 {
|
||||
|
||||
PoolLoop:
|
||||
for _, pool := range PoolServers() {
|
||||
var addr *net.UDPAddr
|
||||
addr, err = net.ResolveUDPAddr("udp", srv.Addr)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
for _, srv := range pool {
|
||||
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
|
||||
}
|
||||
|
||||
var cc *contentConn
|
||||
cc, err = connectContent(conn, srv.Name, userName, srv.MediaPool)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
conns = append(conns, cc)
|
||||
continue PoolLoop
|
||||
}
|
||||
|
||||
var conn *net.UDPConn
|
||||
conn, err = net.DialUDP("udp", nil, addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var cc *contentConn
|
||||
cc, err = connectContent(conn, srv.Name, userName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
conns = append(conns, cc)
|
||||
// There's a pool with no reachable servers.
|
||||
// We can't safely let clients join.
|
||||
return
|
||||
}
|
||||
|
||||
itemDefs, aliases = muxItemDefs(conns)
|
||||
|
@ -594,7 +606,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.mediaPool, &inv[k].InvList.Stacks[i].Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -603,28 +615,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.mediaPool, &cmd.Text2)
|
||||
fallthrough
|
||||
case mt.ImgHUD:
|
||||
fallthrough
|
||||
case mt.ImgWaypointHUD:
|
||||
fallthrough
|
||||
case mt.ImgWaypointHUD + 1:
|
||||
prepend(sc.name, &cmd.Text)
|
||||
prepend(sc.mediaPool, &cmd.Text)
|
||||
}
|
||||
}
|
||||
|
||||
pc := func(cmd *mt.ToCltChangeHUD) {
|
||||
switch t {
|
||||
case mt.StatbarHUD:
|
||||
prepend(sc.name, &cmd.Text2)
|
||||
prepend(sc.mediaPool, &cmd.Text2)
|
||||
fallthrough
|
||||
case mt.ImgHUD:
|
||||
fallthrough
|
||||
case mt.ImgWaypointHUD:
|
||||
fallthrough
|
||||
case mt.ImgWaypointHUD + 1:
|
||||
prepend(sc.name, &cmd.Text)
|
||||
prepend(sc.mediaPool, &cmd.Text)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -117,6 +117,15 @@ Default: ""
|
|||
Description: The network address and port of an internal server.
|
||||
```
|
||||
|
||||
> `Server.MediaPool`
|
||||
```
|
||||
Type: string
|
||||
Default: Server.Name
|
||||
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`
|
||||
```
|
||||
Type: []string
|
||||
|
|
|
@ -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.
|
|
@ -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.mediaPool, &subs[i])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
45
process.go
45
process.go
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/HimbeerserverDE/srp"
|
||||
"github.com/anon55555/mt"
|
||||
|
@ -441,14 +442,28 @@ func (cc *ClientConn) process(pkt mt.Pkt) {
|
|||
srv.swapAOID(&cmd.Pointed.(*mt.PointedAO).ID)
|
||||
}
|
||||
case *mt.ToSrvChatMsg:
|
||||
go func() {
|
||||
done := make(chan struct{})
|
||||
|
||||
go func(done chan<- struct{}) {
|
||||
|
||||
result, isCmd := onChatMsg(cc, cmd)
|
||||
if !isCmd {
|
||||
forward(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
|
||||
}
|
||||
|
@ -616,7 +631,7 @@ func (sc *ServerConn) process(pkt mt.Pkt) {
|
|||
|
||||
handStack := mt.Stack{
|
||||
Item: mt.Item{
|
||||
Name: sc.name + "_hand",
|
||||
Name: sc.mediaPool + "_hand",
|
||||
},
|
||||
Count: 1,
|
||||
}
|
||||
|
@ -734,7 +749,7 @@ func (sc *ServerConn) process(pkt mt.Pkt) {
|
|||
break
|
||||
}
|
||||
|
||||
prepend(sc.name, &cmd.Filename)
|
||||
prepend(sc.mediaPool, &cmd.Filename)
|
||||
if cmd.ShouldCache {
|
||||
cacheMedia(mediaFile{
|
||||
name: cmd.Filename,
|
||||
|
@ -744,17 +759,17 @@ func (sc *ServerConn) process(pkt mt.Pkt) {
|
|||
}
|
||||
case *mt.ToCltSkyParams:
|
||||
for i := range cmd.Textures {
|
||||
prependTexture(sc.name, &cmd.Textures[i])
|
||||
prependTexture(sc.mediaPool, &cmd.Textures[i])
|
||||
}
|
||||
case *mt.ToCltSunParams:
|
||||
prependTexture(sc.name, &cmd.Texture)
|
||||
prependTexture(sc.name, &cmd.ToneMap)
|
||||
prependTexture(sc.name, &cmd.Rise)
|
||||
prependTexture(sc.mediaPool, &cmd.Texture)
|
||||
prependTexture(sc.mediaPool, &cmd.ToneMap)
|
||||
prependTexture(sc.mediaPool, &cmd.Rise)
|
||||
case *mt.ToCltMoonParams:
|
||||
prependTexture(sc.name, &cmd.Texture)
|
||||
prependTexture(sc.name, &cmd.ToneMap)
|
||||
prependTexture(sc.mediaPool, &cmd.Texture)
|
||||
prependTexture(sc.mediaPool, &cmd.ToneMap)
|
||||
case *mt.ToCltSetHotbarParam:
|
||||
prependTexture(sc.name, &cmd.Img)
|
||||
prependTexture(sc.mediaPool, &cmd.Img)
|
||||
case *mt.ToCltUpdatePlayerList:
|
||||
if !clt.playerListInit {
|
||||
clt.playerListInit = true
|
||||
|
@ -772,7 +787,7 @@ func (sc *ServerConn) process(pkt mt.Pkt) {
|
|||
}
|
||||
}
|
||||
case *mt.ToCltSpawnParticle:
|
||||
prependTexture(sc.name, &cmd.Texture)
|
||||
prependTexture(sc.mediaPool, &cmd.Texture)
|
||||
sc.globalParam0(&cmd.NodeParam0)
|
||||
case *mt.ToCltBlkData:
|
||||
for i := range cmd.Blk.Param0 {
|
||||
|
@ -791,14 +806,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.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.name, &cmd.Name)
|
||||
prepend(sc.mediaPool, &cmd.Name)
|
||||
sc.swapAOID(&cmd.SrcAOID)
|
||||
if cmd.Loop {
|
||||
sc.sounds[cmd.ID] = struct{}{}
|
||||
|
@ -823,7 +838,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.mediaPool, &cmd.Modes[i].Texture)
|
||||
}
|
||||
case *mt.ToCltNodeMetasChanged:
|
||||
for k := range cmd.Changed {
|
||||
|
|
2
proxy.go
2
proxy.go
|
@ -16,7 +16,7 @@ import (
|
|||
const (
|
||||
latestSerializeVer = 28
|
||||
latestProtoVer = 39
|
||||
versionString = "5.4.1-dev-b2596eda3"
|
||||
versionString = "5.4.1"
|
||||
maxPlayerNameLen = 20
|
||||
bytesPerMediaBunch = 5000
|
||||
)
|
||||
|
|
|
@ -29,6 +29,8 @@ type ServerConn struct {
|
|||
salt, srpA, a, srpK []byte
|
||||
}
|
||||
|
||||
mediaPool string
|
||||
|
||||
inv mt.Inv
|
||||
detachedInvs []string
|
||||
|
||||
|
|
Loading…
Reference in New Issue