diff options
Diffstat (limited to 'server/commands.go')
| -rw-r--r-- | server/commands.go | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/server/commands.go b/server/commands.go index abb4e43..0b56aaf 100644 --- a/server/commands.go +++ b/server/commands.go @@ -1 +1,223 @@ package server + +import ( + "sort" + "strings" +) + +type CommandSender interface { + OnCommandOutput(from *Server, output string) + OnCommandError(from *Server, err string) +} +type commandCtx struct { + server *Server + sender CommandSender + arg arguments + auth authLevel +} +type commandHandler func(commandCtx) (err string) + +func isCommand(message string) (isCmd bool, text string) { + text = message + if message[0] == '/' { + text = message[1:] + // an additional slash escapes the second //not command -> /not command + isCmd = len(text) != 0 && text[0] != '/' + } + return +} + +func executeCommand( + s *Server, auth authLevel, from CommandSender, command string) { + ctx := commandCtx { + server: s, + sender: from, + arg: parseArgs(command), + auth: auth, + } + cmd, _ := ctx.arg.nextArg() + handler := commands[cmd] + var err string + if commandAuth[cmd] > auth { + err = "Permission denied" + } else if handler == nil { + err = "Unknown command: /" + cmd + } else { + err = handler(ctx) + } + if err != "" { + from.OnCommandError(s, err) + } +} + +func usage(cmd string) string { + if help[cmd] != nil { + return "Usage: " + help[cmd][0] + } else { + return "Invalid usage" + } +} + +var commands = map[string]commandHandler { + "op": func(ctx commandCtx) string { + name, ok := ctx.arg.nextArg() + if !ok { + return usage("op") + } + if !playerNameRegex.Match([]byte(name)) { + return "Unknown player." + } + ctx.server.changePlayerAuth(name, opAuth, func(ok bool) { + if ok { + ctx.sender.OnCommandOutput(ctx.server, "Opped.") + } else { + ctx.sender.OnCommandError(ctx.server, "Unknown player") + } + }) + return "" + }, + "deop": func(ctx commandCtx) string { + name, ok := ctx.arg.nextArg() + if !ok { + return usage("op") + } + ctx.server.changePlayerAuth(name, defaultAuth, func(ok bool) { + if ok { + ctx.sender.OnCommandOutput(ctx.server, "Deopped.") + } else { + ctx.sender.OnCommandError(ctx.server, "Unknown player") + } + }) + return "" + }, + "stop": func(ctx commandCtx) string { + ctx.server.Stop(nil) + return "" + }, + "save": func(ctx commandCtx) string { + ctx.server.Save(nil) + return "" + }, + "help": func(ctx commandCtx) string { + cmd, ok := ctx.arg.nextArg() + if !ok { + var usages []string + for cmd, helpMessages := range help { + if commandAuth[cmd] <= ctx.auth { + usages = append(usages, helpMessages[0]) + } + } + sort.Strings(usages) + ctx.sender.OnCommandOutput(ctx.server, "Available commands:") + for _, usage := range usages { + ctx.sender.OnCommandOutput(ctx.server, "&7* &e" + usage) + } + return "" + } + if help[cmd] == nil { + return "Unknown command: /" + cmd + } + for n, line := range help[cmd] { + if n == 0 { + line = "Usage: " + line + } else { + line = " " + line + } + ctx.sender.OnCommandOutput(ctx.server, line) + } + return "" + }, +} + +var commandAuth = map[string]authLevel { + "op": opAuth, + "deop": opAuth, + "save": opAuth, + "stop": opAuth, +} + +var help = map[string][]string { + "op": []string { + "/op <player>", + "Grant operator status", + }, + "deop": []string { + "/deop <player>", + "Revoke operator status", + }, + "save": []string { + "/save", + "Save all levels", + }, + "stop": []string { + "/stop", + "Stop the server", + }, + "help": []string { + "/help [command]", + }, +} + +type arguments struct { + rd *strings.Reader +} + +func parseArgs(command string) arguments { + return arguments {strings.NewReader(command)} +} + +func (arg *arguments) nextArg() (string, bool) { + var out strings.Builder + for { + b, err := arg.rd.ReadByte() + if err != nil { + return "", false + } + if b != ' ' { + arg.rd.UnreadByte() + break + } + } + var splitChar byte = ' ' + b, err := arg.rd.ReadByte() + if err != nil { + return "", false + } + if b == '"' { + splitChar = '"' + } else { + arg.rd.UnreadByte() + } + for { + b, err = arg.rd.ReadByte() + if err != nil { + if out.Len() > 0 { + break + } + return "", false + } + if b == splitChar { + break + } + if b == '\\' { + b, err = arg.rd.ReadByte() + if err != nil { + b = '\\' + } + } + out.WriteByte(b) + } + return out.String(), true +} + +func (arg *arguments) allArgs() []string { + var (a string; ok bool; args []string) + for { + a, ok = arg.nextArg() + if !ok { + break + } + args = append(args, a) + } + return args +} |
