diff --git a/server/session/command.go b/server/session/command.go index 4a1f7be9c..498c7b5ef 100644 --- a/server/session/command.go +++ b/server/session/command.go @@ -47,7 +47,7 @@ type translation interface { // sendAvailableCommands sends all available commands of the server. Once sent, they will be visible in the // /help list and will be auto-completed. -func (s *Session) sendAvailableCommands(co Controllable) map[string]map[int]cmd.Runnable { +func (s *Session) sendAvailableCommands(co Controllable, softEnums map[string]struct{}) map[string]map[int]cmd.Runnable { commands := cmd.Commands() m := make(map[string]map[int]cmd.Runnable, len(commands)) @@ -84,32 +84,9 @@ func (s *Session) sendAvailableCommands(co Controllable) map[string]map[int]cmd. for i, params := range params { for _, paramInfo := range params { t, enum := valueToParamType(paramInfo, co) - t |= protocol.CommandArgValid suffix := paramInfo.Suffix opt := byte(0) - if _, ok := paramInfo.Value.(bool); ok { - opt |= protocol.ParamOptionCollapseEnum - } - if len(enum.Options) > 0 || enum.Type != "" { - if !enum.Dynamic { - index, ok := enumIndices[enum.Type] - if !ok { - index = uint32(len(enums)) - enumIndices[enum.Type] = index - enums = append(enums, enum) - } - t |= protocol.CommandArgEnum | index - } else { - index, ok := dynamicEnumIndices[enum.Type] - if !ok { - index = uint32(len(dynamicEnums)) - dynamicEnumIndices[enum.Type] = index - dynamicEnums = append(dynamicEnums, enum) - } - t |= protocol.CommandArgSoftEnum | index - } - } if suffix != "" { index, ok := suffixIndices[suffix] if !ok { @@ -117,7 +94,29 @@ func (s *Session) sendAvailableCommands(co Controllable) map[string]map[int]cmd. suffixIndices[suffix] = index pk.Suffixes = append(pk.Suffixes, suffix) } - t |= protocol.CommandArgSuffixed | index + t = protocol.CommandArgSuffixed | index + } else { + t |= protocol.CommandArgValid + if len(enum.Options) > 0 || enum.Type != "" { + _, dynamic := softEnums[enum.Type] + if !dynamic { + index, ok := enumIndices[enum.Type] + if !ok { + index = uint32(len(enums)) + enumIndices[enum.Type] = index + enums = append(enums, enum) + } + t |= protocol.CommandArgEnum | index + } else { + index, ok := dynamicEnumIndices[enum.Type] + if !ok { + index = uint32(len(dynamicEnums)) + dynamicEnumIndices[enum.Type] = index + dynamicEnums = append(dynamicEnums, enum) + } + t |= protocol.CommandArgSoftEnum | index + } + } } overloads[i].Parameters = append(overloads[i].Parameters, protocol.CommandParameter{ Name: paramInfo.Name, @@ -164,7 +163,6 @@ func (s *Session) sendAvailableCommands(co Controllable) map[string]map[int]cmd. type commandEnum struct { Type string Options []string - Dynamic bool } // valueToParamType finds the command argument type of the value passed and returns it, in addition to creating @@ -183,8 +181,8 @@ func valueToParamType(i cmd.ParamInfo, source cmd.Source) (t uint32, enum comman return protocol.CommandArgTypeTarget, enum case bool: return 0, commandEnum{ - Type: "bool", - Options: []string{"true", "1", "false", "0"}, + Type: "Boolean", + Options: []string{"true", "false"}, } case mgl64.Vec3: return protocol.CommandArgTypePosition, enum @@ -198,7 +196,6 @@ func valueToParamType(i cmd.ParamInfo, source cmd.Source) (t uint32, enum comman return 0, commandEnum{ Type: enum.Type(), Options: enum.Options(source), - Dynamic: true, } } return protocol.CommandArgTypeValue, enum @@ -207,7 +204,11 @@ func valueToParamType(i cmd.ParamInfo, source cmd.Source) (t uint32, enum comman // resendCommands resends all commands that a Session has access to if the map of runnable commands passed does not // match with the commands that the Session is currently allowed to execute. // True is returned if the commands were resent. -func (s *Session) resendCommands(before map[string]map[int]cmd.Runnable, co Controllable) (map[string]map[int]cmd.Runnable, bool) { +func (s *Session) resendCommands( + before map[string]map[int]cmd.Runnable, + co Controllable, + softEnums map[string]struct{}, +) (map[string]map[int]cmd.Runnable, bool) { commands := cmd.Commands() m := make(map[string]map[int]cmd.Runnable, len(commands)) @@ -219,13 +220,13 @@ func (s *Session) resendCommands(before map[string]map[int]cmd.Runnable, co Cont } } if len(before) != len(m) { - return s.sendAvailableCommands(co), true + return s.sendAvailableCommands(co, softEnums), true } // First check for commands that were newly added. for name, r := range m { for k := range r { if _, ok := before[name][k]; !ok { - return s.sendAvailableCommands(co), true + return s.sendAvailableCommands(co, softEnums), true } } } @@ -233,7 +234,7 @@ func (s *Session) resendCommands(before map[string]map[int]cmd.Runnable, co Cont for name, r := range before { for k := range r { if _, ok := m[name][k]; !ok { - return s.sendAvailableCommands(co), true + return s.sendAvailableCommands(co, softEnums), true } } } @@ -259,22 +260,41 @@ func (s *Session) enums(co Controllable) (map[string]cmd.Enum, map[string][]stri } // resendEnums checks the options of the enums passed against the values that were previously recorded. If they do not -// match, the enum is resent to the client and the values are updated in the before map. -func (s *Session) resendEnums(enums map[string]cmd.Enum, before map[string][]string, c Controllable) { +// match, and the enum is in softEnums, the enum is resent via UpdateSoftEnum. If the enum is not yet in softEnums, +// it is added and the full AvailableCommands packet is resent. +func (s *Session) resendEnums( + enums map[string]cmd.Enum, + before map[string][]string, + softEnums map[string]struct{}, + r map[string]map[int]cmd.Runnable, + c Controllable, +) map[string]map[int]cmd.Runnable { for name, enum := range enums { valuesBefore := before[name] values := enum.Options(c) before[name] = values + changed := false if len(valuesBefore) != len(values) { - s.writePacket(&packet.UpdateSoftEnum{EnumType: name, Options: values, ActionType: packet.SoftEnumActionSet}) + changed = true + } else { + for k, v := range values { + if valuesBefore[k] != v { + changed = true + break + } + } + } + if !changed { continue } - for k, v := range values { - if valuesBefore[k] != v { - s.writePacket(&packet.UpdateSoftEnum{EnumType: name, Options: values, ActionType: packet.SoftEnumActionSet}) - break - } + + if _, dynamic := softEnums[name]; dynamic { + s.writePacket(&packet.UpdateSoftEnum{EnumType: name, Options: values, ActionType: packet.SoftEnumActionSet}) + } else { + softEnums[name] = struct{}{} + r = s.sendAvailableCommands(c, softEnums) } } + return r } diff --git a/server/session/session.go b/server/session/session.go index f63988a02..c3518aff2 100644 --- a/server/session/session.go +++ b/server/session/session.go @@ -370,13 +370,14 @@ func (s *Session) background() { r map[string]map[int]cmd.Runnable enums map[string]cmd.Enum enumValues map[string][]string + softEnums = make(map[string]struct{}) ok bool i int ) s.ent.ExecWorld(func(tx *world.Tx, e world.Entity) { co := e.(Controllable) - r = s.sendAvailableCommands(co) + r = s.sendAvailableCommands(co, softEnums) enums, enumValues = s.enums(co) }) @@ -391,11 +392,11 @@ func (s *Session) background() { if i++; i%20 == 0 { // Enum resending happens relatively often and frequent updates are more important than with full // command changes. Those are generally only related to permission changes, which doesn't happen often. - s.resendEnums(enums, enumValues, c) + r = s.resendEnums(enums, enumValues, softEnums, r, c) } if i%100 == 0 { // Try to resend commands only every 5 seconds. - if r, ok = s.resendCommands(r, c); ok { + if r, ok = s.resendCommands(r, c, softEnums); ok { enums, enumValues = s.enums(c) } }