From 1e079dcccf2299725e4013e3805b116145e7b9e1 Mon Sep 17 00:00:00 2001 From: Trevor Slocum Date: Fri, 19 Jul 2024 17:32:30 -0700 Subject: [PATCH] Implement help command Resolves #18. --- PROTOCOL.md | 13 ++++++++++--- command.go | 29 +++++++++++++++++++++++++++++ event.go | 8 -------- pkg/server/client.go | 6 ------ pkg/server/server.go | 11 +++++++++++ pkg/server/server_command.go | 20 +++++++++++++++----- 6 files changed, 65 insertions(+), 22 deletions(-) diff --git a/PROTOCOL.md b/PROTOCOL.md index a65a470..fdad310 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -68,14 +68,17 @@ formatted responses are more easily parsed by computers. - List all matches. - Aliases: `ls` -- `create / [name]` - - Create a match. An `acey` value of 0 represents a standard game, while a value of 1 represents an acey-deucey game. +- `create / [name]` + - Create a match. A `variant` value of 0 represents a standard game, a value of 1 represents an acey-deucey game and a value of 2 represents a tabula game. - Aliases: `c` - `join / [password]` - Join match by match ID or by player. - Aliases: `j` +- `leave` + - Leave match. + - `double` - Offer double to opponent. - Aliases: `d` @@ -123,7 +126,11 @@ must write some data to the server at least once every 40 seconds. - `disconnect` - Disconnect from the server. -- `broadcast` +- `motd [message]` + - View (or set) message of the day. + - Specifying a new message of the day is only available to server administrators. + +- `broadcast ` - Send a message to all players. - This command is only available to server administrators. diff --git a/command.go b/command.go index 96a780e..4c94683 100644 --- a/command.go +++ b/command.go @@ -58,3 +58,32 @@ const ( EventTypeReplay = "replay" EventTypeHistory = "history" ) + +var HelpText = map[string]string{ + CommandLogin: "[username] [password] - Log in. A random username is assigned when none is provided.", + CommandRegister: " - Register an account. A valid email address must be provided.", + CommandResetPassword: " - Request a password reset link via email.", + CommandPassword: " - Change account password.", + CommandSet: " - Change account setting. Available settings: highlight, pips and moves.", + CommandReplay: " - Retrieve replay of the specified game.", + CommandHistory: " [page] - Retrieve match history of the specified player.", + CommandHelp: "[command] - Request help for all commands, or optionally a specific command.", + CommandSay: " - Send a chat message. This command can only be used after creating or joining a match.", + CommandList: "- List all matches.", + CommandCreate: "/ [name] - Create a match. A variant value of 0 represents a standard game, a value of 1 represents an acey-deucey game and a value of 2 represents a tabula game.", + CommandJoin: "/ [password] - Join match by match ID or by player.", + CommandLeave: "- Leave match.", + CommandDouble: "- Offer double to opponent.", + CommandResign: "- Resign game. Resigning when a double is offered will decline the offer.", + CommandRoll: "- Roll dice.", + CommandMove: " [from-to]... - Move checkers.", + CommandReset: "- Reset pending checker movement.", + CommandOk: "[1-6] - Accept double offer or confirm checker movement. The parameter for this command only applies in acey-deucey games.", + CommandRematch: "- Request (or accept) a rematch after a match has been finished.", + CommandBoard: "- Request current match state.", + CommandPong: " - Sent in response to server ping event to prevent the connection from timing out.", + CommandDisconnect: "- Disconnect from the server.", + CommandMOTD: "[message] - View (or set) message of the day. Specifying a new message of the day is only available to server administrators.", + CommandBroadcast: " - Send a message to all players. This command is only available to server administrators.", + CommandShutdown: " - Prevent the creation of new matches and periodically warn players about the server shutting down. This command is only available to server administrators.", +} diff --git a/event.go b/event.go index 713a08e..1b323a3 100644 --- a/event.go +++ b/event.go @@ -24,12 +24,6 @@ type EventWelcome struct { Games int } -type EventHelp struct { - Event - Topic string - Message string -} - type EventPing struct { Event Message string @@ -169,8 +163,6 @@ func DecodeEvent(message []byte) (interface{}, error) { switch e.Type { case EventTypeWelcome: ev = &EventWelcome{} - case EventTypeHelp: - ev = &EventHelp{} case EventTypePing: ev = &EventPing{} case EventTypeNotice: diff --git a/pkg/server/client.go b/pkg/server/client.go index 331a4c0..66a52f4 100644 --- a/pkg/server/client.go +++ b/pkg/server/client.go @@ -95,8 +95,6 @@ func (c *serverClient) sendEvent(e interface{}) { switch ev := e.(type) { case *bgammon.EventWelcome: ev.Type = bgammon.EventTypeWelcome - case *bgammon.EventHelp: - ev.Type = bgammon.EventTypeHelp case *bgammon.EventPing: ev.Type = bgammon.EventTypePing case *bgammon.EventNotice: @@ -149,10 +147,6 @@ func (c *serverClient) sendEvent(e interface{}) { switch ev := e.(type) { case *bgammon.EventWelcome: c.Write([]byte(fmt.Sprintf("welcome %s there are %d clients playing %d matches.", ev.PlayerName, ev.Clients, ev.Games))) - case *bgammon.EventHelp: - c.Write([]byte("helpstart Help text:")) - c.Write([]byte(fmt.Sprintf("help %s", ev.Message))) - c.Write([]byte("helpend End of help text.")) case *bgammon.EventPing: c.Write([]byte(fmt.Sprintf("ping %s", ev.Message))) case *bgammon.EventNotice: diff --git a/pkg/server/server.go b/pkg/server/server.go index d71c36d..b12aa74 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -15,7 +15,9 @@ import ( "net/http" "os" "os/exec" + "reflect" "regexp" + "sort" "strconv" "strings" "sync" @@ -78,6 +80,8 @@ type server struct { motd string + sortedCommands []string + mailServer string passwordSalt string resetSalt string @@ -108,6 +112,13 @@ func NewServer(tz string, dataSource string, mailServer string, passwordSalt str } s.loadLocales() + keys := reflect.ValueOf(bgammon.HelpText).MapKeys() + sortKeys := func(i, j int) bool { return keys[i].Interface().(string) < keys[j].Interface().(string) } + sort.Slice(keys, sortKeys) + for _, key := range keys { + s.sortedCommands = append(s.sortedCommands, key.Interface().(string)) + } + if tz != "" { var err error s.tz, err = time.LoadLocation(tz) diff --git a/pkg/server/server_command.go b/pkg/server/server_command.go index 1404bdf..4852fc8 100644 --- a/pkg/server/server_command.go +++ b/pkg/server/server_command.go @@ -257,11 +257,21 @@ COMMANDS: switch keyword { case bgammon.CommandHelp, "h": - // TODO get extended help by specifying a command after help - cmd.client.sendEvent(&bgammon.EventHelp{ - Topic: "", - Message: "Test help text", - }) + if len(params) > 0 { + command := string(bytes.ToLower(bytes.Join(params, []byte(" ")))) + commandHelp := bgammon.HelpText[command] + if commandHelp != "" { + cmd.client.sendNotice("/" + command + " " + commandHelp) + } else { + cmd.client.sendNotice(fmt.Sprintf("Unknown command: %s", command)) + } + continue + } + + cmd.client.sendNotice("Available commands:") + for _, command := range s.sortedCommands { + cmd.client.sendNotice("/" + command + " " + bgammon.HelpText[command]) + } case bgammon.CommandJSON: sendUsage := func() { cmd.client.sendNotice("To enable JSON formatted messages, send 'json on'. To disable JSON formatted messages, send 'json off'.")