Skip to content

Commit 8b8c52f

Browse files
committed
re-validating config after changes are made. Finalizing admin "server config" command
1 parent 283cef4 commit 8b8c52f

File tree

3 files changed

+232
-8
lines changed

3 files changed

+232
-8
lines changed

_datafiles/config.yaml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,11 @@ Server:
7777
# It is a good idea to lock configs related to folder/file paths to prevent
7878
# accidental changes that could break the game.
7979
Locked:
80-
- DataFiles
81-
- PublicHtml
82-
- AdminHtml
83-
- NextRoomId
84-
- Seed
85-
- OnLoginCommands
86-
- BannedNames
80+
- FilePaths
81+
- Server.NextRoomId
82+
- Server.Seed
83+
- Server.OnLoginCommands
84+
- Server.BannedNames
8785

8886
################################################################################
8987
#

internal/configs/configs.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,12 @@ func SetVal(propertyPath string, newVal string) error {
317317
return err
318318
}
319319

320-
return configData.OverlayOverrides(overrides)
320+
if err = configData.OverlayOverrides(overrides); err != nil {
321+
return err
322+
}
323+
324+
configData.Validate()
325+
return nil
321326
}
322327

323328
func GetConfig() Config {

internal/usercommands/admin.server.go

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package usercommands
22

33
import (
4+
"errors"
45
"fmt"
56
"slices"
67
"sort"
@@ -18,6 +19,7 @@ import (
1819

1920
var (
2021
memoryReportCache = map[string]util.MemoryResult{}
22+
errValueLocked = errors.New("This config value is locked. You must edit the config file directly.")
2123
)
2224

2325
/*
@@ -33,6 +35,10 @@ func Server(rest string, user *users.UserRecord, room *rooms.Room, flags events.
3335
}
3436

3537
args := util.SplitButRespectQuotes(rest)
38+
if args[0] == "config" {
39+
return server_Config(strings.TrimSpace(rest[1:]), user, room, flags)
40+
}
41+
3642
if args[0] == "set" {
3743

3844
args = args[1:]
@@ -287,3 +293,218 @@ func Server(rest string, user *users.UserRecord, room *rooms.Room, flags events.
287293

288294
return true, nil
289295
}
296+
297+
func server_Config(_ string, user *users.UserRecord, room *rooms.Room, flags events.EventFlag) (bool, error) {
298+
299+
// Get if already exists, otherwise create new
300+
cmdPrompt, isNew := user.StartPrompt(`server config`, "")
301+
302+
if isNew {
303+
user.SendText(``)
304+
menuOptions, _ := getConfigOptions("")
305+
tplTxt, _ := templates.Process("tables/numbered-list", menuOptions, user.UserId)
306+
user.SendText(tplTxt)
307+
}
308+
309+
configPrefix := ""
310+
if selection, ok := cmdPrompt.Recall("config-selected"); ok {
311+
configPrefix = selection.(string)
312+
}
313+
314+
if configPrefix != "" {
315+
allConfigData := configs.GetConfig().AllConfigData()
316+
if configVal, ok := allConfigData[configPrefix]; ok {
317+
318+
if !isEditAllowed(configPrefix) {
319+
user.SendText(errValueLocked.Error())
320+
user.ClearPrompt()
321+
return true, nil
322+
}
323+
324+
question := cmdPrompt.Ask(`New Value for `+configPrefix, []string{fmt.Sprintf("%v", configVal)}, fmt.Sprintf("%v", configVal))
325+
if !question.Done {
326+
return true, nil
327+
}
328+
329+
user.ClearPrompt()
330+
331+
err := configs.SetVal(configPrefix, question.Response)
332+
if err == nil {
333+
user.SendText(configPrefix + " has been set to: " + question.Response)
334+
return true, nil
335+
}
336+
user.SendText(err.Error())
337+
return true, nil
338+
}
339+
}
340+
341+
question := cmdPrompt.Ask(`Choose a config option, or "quit"`, []string{``}, ``)
342+
if !question.Done {
343+
return true, nil
344+
}
345+
346+
if question.Response == "quit" {
347+
user.SendText("Quitting...")
348+
user.ClearPrompt()
349+
return true, nil
350+
}
351+
352+
fullPath := strings.ToLower(configPrefix)
353+
if fullPath != `` {
354+
fullPath += "."
355+
}
356+
fullPath += question.Response
357+
358+
if !isEditAllowed(fullPath) {
359+
user.SendText(errValueLocked.Error())
360+
question.RejectResponse()
361+
return true, nil
362+
}
363+
364+
menuOptions, ok := getConfigOptions(fullPath)
365+
if !ok {
366+
question.RejectResponse()
367+
menuOptions, _ = getConfigOptions("")
368+
} else {
369+
370+
if len(menuOptions) == 1 {
371+
fullPath = menuOptions[0].Id.(string)
372+
373+
cmdPrompt.Store("config-selected", fullPath)
374+
375+
if !isEditAllowed(fullPath) {
376+
user.SendText(errValueLocked.Error())
377+
user.ClearPrompt()
378+
return true, nil
379+
}
380+
381+
allConfigData := configs.GetConfig().AllConfigData()
382+
if configVal, ok := allConfigData[fullPath]; ok {
383+
cmdPrompt.Ask(`New Value for `+fullPath, []string{fmt.Sprintf("%v", configVal)}, fmt.Sprintf("%v", configVal))
384+
return true, nil
385+
}
386+
}
387+
388+
cmdPrompt.Store("config-selected", fullPath)
389+
}
390+
391+
tplTxt, _ := templates.Process("tables/numbered-list", menuOptions, user.UserId)
392+
user.SendText(tplTxt)
393+
394+
question.RejectResponse()
395+
396+
return true, nil
397+
}
398+
399+
func isEditAllowed(configPath string) bool {
400+
401+
configPath = strings.ToLower(configPath)
402+
403+
if strings.HasSuffix(configPath, "locked") {
404+
return false
405+
}
406+
407+
sc := configs.GetServerConfig()
408+
for _, v := range sc.Locked {
409+
if strings.HasPrefix(configPath, strings.ToLower(v)) {
410+
return false
411+
}
412+
}
413+
414+
return true
415+
}
416+
417+
func getConfigOptions(input string) ([]templates.NameDescription, bool) {
418+
419+
input = strings.ToLower(input)
420+
421+
configOptions := []templates.NameDescription{}
422+
423+
allConfigData := configs.GetConfig().AllConfigData()
424+
pathLookup := map[string]string{}
425+
for name, _ := range allConfigData {
426+
427+
lowerName := strings.ToLower(name)
428+
pathLookup[lowerName] = name
429+
430+
builtPath := ""
431+
for _, namePart := range strings.Split(name, ".") {
432+
builtPath += namePart
433+
if _, ok := pathLookup[builtPath]; !ok {
434+
pathLookup[strings.ToLower(builtPath)] = builtPath
435+
}
436+
builtPath += "."
437+
}
438+
}
439+
440+
inputProperCase := input
441+
if caseCheck, ok := pathLookup[input]; ok {
442+
443+
inputProperCase = caseCheck
444+
445+
// Is this a full config path?
446+
if configVal, ok := allConfigData[inputProperCase]; ok {
447+
448+
configOptions = append(configOptions, templates.NameDescription{
449+
Id: inputProperCase,
450+
Name: inputProperCase,
451+
Description: fmt.Sprintf("%v", configVal),
452+
})
453+
454+
return configOptions, true
455+
456+
}
457+
458+
} else if input != "" {
459+
return configOptions, false
460+
}
461+
462+
// Find which partial path we are on and populate options
463+
usedNames := map[string]struct{}{}
464+
for fullConfigPath, configVal := range allConfigData {
465+
466+
if input != "" {
467+
if len(fullConfigPath) <= len(input) || fullConfigPath[0:len(inputProperCase)] != inputProperCase {
468+
continue
469+
}
470+
}
471+
472+
nextConfigPathSection := fullConfigPath
473+
if len(inputProperCase) > 0 {
474+
nextConfigPathSection = nextConfigPathSection[len(inputProperCase)+1:]
475+
}
476+
477+
desc := "..."
478+
if dotIdx := strings.Index(nextConfigPathSection, "."); dotIdx != -1 {
479+
nextConfigPathSection = nextConfigPathSection[:dotIdx]
480+
} else {
481+
desc = fmt.Sprintf("%v", configVal)
482+
}
483+
484+
if _, ok := usedNames[nextConfigPathSection]; ok {
485+
continue
486+
}
487+
488+
usedNames[nextConfigPathSection] = struct{}{}
489+
490+
pathWithSection := nextConfigPathSection
491+
if len(inputProperCase) > 0 {
492+
pathWithSection = inputProperCase + "." + pathWithSection
493+
}
494+
495+
configOptions = append(configOptions, templates.NameDescription{
496+
Id: pathWithSection,
497+
Name: nextConfigPathSection,
498+
Description: desc,
499+
})
500+
501+
}
502+
503+
if len(configOptions) > 0 {
504+
sort.Slice(configOptions, func(i, j int) bool {
505+
return configOptions[i].Name < configOptions[j].Name
506+
})
507+
}
508+
509+
return configOptions, true
510+
}

0 commit comments

Comments
 (0)