Add defcon command

Resolves #26.
This commit is contained in:
Trevor Slocum 2024-08-23 22:39:33 -07:00
parent 92a9180ed2
commit 1de6f6e367
6 changed files with 103 additions and 2 deletions

View file

@ -140,6 +140,16 @@ must write some data to the server at least once every 40 seconds.
- Send a message to all players.
- This command is only available to server administrators.
- `defcon [level]`
- Apply restrictions to guests to prevent abuse.
- This command is only available to server administrators and moderators.
- Levels:
1. Disallow new accounts from being registered.
2. Only registered users may create and join matches.
3. Only registered users may chat and set custom match titles.
4. Warning message is broadcast to all users.
5. Normal operation.
- `shutdown <minutes> <reason>`
- Prevent the creation of new matches and periodically warn players about the server shutting down.
- This command is only available to server administrators.

View file

@ -33,6 +33,7 @@ const (
CommandDisconnect = "disconnect" // Disconnect from server.
CommandMOTD = "motd" // Read (or write) the message of the day.
CommandBroadcast = "broadcast" // Send a message to all players.
CommandDefcon = "defcon" // Apply restrictions to guests to prevent abuse.
CommandShutdown = "shutdown" // Prevent the creation of new matches.
)
@ -82,12 +83,13 @@ var HelpText = map[string]string{
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.",
CommandFollow: "- Follow a player. A notification is shown whenever a followed player goes online or offline.",
CommandUnfollow: "- Un-follow a player.",
CommandFollow: "<username> - Follow a player. A notification is shown whenever a followed player goes online or offline.",
CommandUnfollow: "<username> - Un-follow a player.",
CommandBoard: "- Request current match state.",
CommandPong: "<message> - 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: "<message> - Send a message to all players. This command is only available to server administrators.",
CommandDefcon: "[level] - Apply restrictions to guests to prevent abuse. Levels:\n1. Disallow new accounts from being registered.\n2. Only registered users may create and join matches.\n3. Only registered users may chat and set custom match titles.\n4. Warning message is broadcast to all users.\n5. Normal operation.",
CommandShutdown: "<minutes> <reason> - Prevent the creation of new matches and periodically warn players about the server shutting down. This command is only available to server administrators.",
}

View file

@ -89,6 +89,10 @@ func (c *serverClient) Admin() bool {
return c.accountID == 1
}
func (c *serverClient) Mod() bool {
return false
}
func (c *serverClient) sendEvent(e interface{}) {
// JSON formatted messages.
if c.json {
@ -210,6 +214,20 @@ func (c *serverClient) sendBroadcast(message string) {
})
}
func (c *serverClient) sendDefconWarning(defcon int) {
var message string
if defcon == 4 {
message = gotext.GetD(c.language, "Due to ongoing abuse, some actions may become restricted to registered users only. Please log in or register to avoid interruptions.")
} else if defcon < 4 {
message = gotext.GetD(c.language, "Due to ongoing abuse, some actions are restricted to registered users only. Please log in or register to avoid interruptions.")
} else {
return
}
c.sendEvent(&bgammon.EventNotice{
Message: gotext.GetD(c.language, "SERVER BROADCAST:") + " " + message,
})
}
func (c *serverClient) label() string {
if len(c.name) > 0 {
return string(c.name)

View file

@ -49,6 +49,15 @@ msgstr ""
msgid "Double offered to opponent (%d points)."
msgstr ""
msgid "Due to ongoing abuse, registration is disabled. Please try again later."
msgstr ""
msgid "Due to ongoing abuse, some actions are restricted to registered users only. Please log in or register to avoid interruptions."
msgstr ""
msgid "Due to ongoing abuse, some actions may become restricted to registered users only. Please log in or register to avoid interruptions."
msgstr ""
msgid "Failed to change password."
msgstr ""

View file

@ -78,6 +78,8 @@ type server struct {
leaderboardCacheTime [12]time.Time
leaderboardCacheLock sync.Mutex
defcon int
motd string
sortedCommands []string
@ -104,6 +106,7 @@ func NewServer(tz string, dataSource string, mailServer string, passwordSalt str
newClientIDs: make(chan int),
commands: make(chan serverCommand, bufferSize),
welcome: []byte("hello Welcome to bgammon.org! Please log in by sending the 'login' command. You may specify a username, otherwise you will be assigned a random username. If you specify a username, you may also specify a password. Have fun!"),
defcon: 5,
mailServer: mailServer,
passwordSalt: passwordSalt,
resetSalt: resetSalt,

View file

@ -38,6 +38,11 @@ func (s *server) handleFirstCommand(cmd serverCommand, keyword string, params []
cmd.client.Terminate(gotext.GetD(cmd.client.language, "Please enter an email, username and password."))
}
if s.defcon == 1 {
cmd.client.Terminate(gotext.GetD(cmd.client.language, "Due to ongoing abuse, registration is disabled. Please try again later."))
return
}
var email []byte
if keyword == bgammon.CommandRegisterJSON || keyword == "rj" {
if len(params) < 4 {
@ -221,6 +226,11 @@ func (s *server) handleFirstCommand(cmd serverCommand, keyword string, params []
}
}
// Send DEFCON warning message.
if s.defcon != 5 {
cmd.client.sendDefconWarning(s.defcon)
}
// Send followed player notifications.
c := cmd.client
if c.accountID != 0 {
@ -384,6 +394,10 @@ COMMANDS:
cmd.client.sendNotice(gotext.GetD(cmd.client.language, "Message not sent: There is no one else in the match."))
continue
}
if s.defcon < 4 && cmd.client.accountID == 0 {
cmd.client.sendNotice(gotext.GetD(cmd.client.language, "Due to ongoing abuse, some actions are restricted to registered users only. Please log in or register to avoid interruptions."))
continue
}
ev := &bgammon.EventSay{
Message: string(bytes.Join(params, []byte(" "))),
}
@ -413,6 +427,11 @@ COMMANDS:
continue
}
if s.defcon < 3 && cmd.client.accountID == 0 {
cmd.client.sendNotice(gotext.GetD(cmd.client.language, "Due to ongoing abuse, some actions are restricted to registered users only. Please log in or register to avoid interruptions."))
continue
}
var gamePassword []byte
gameType := bytes.ToLower(params[0])
var gameName []byte
@ -465,6 +484,10 @@ COMMANDS:
points = 127
}
if s.defcon < 4 && cmd.client.accountID == 0 {
gameName = nil
}
// Set default game name.
if len(bytes.TrimSpace(gameName)) == 0 {
abbr := "'s"
@ -507,6 +530,13 @@ COMMANDS:
continue
}
if s.defcon < 3 && cmd.client.accountID == 0 {
cmd.client.sendEvent(&bgammon.EventFailedJoin{
Reason: gotext.GetD(cmd.client.language, "Due to ongoing abuse, some actions are restricted to registered users only. Please log in or register to avoid interruptions."),
})
continue
}
var joinGameID int
if onlyNumbers.Match(params[0]) {
gameID, err := strconv.Atoi(string(params[0]))
@ -1411,6 +1441,35 @@ COMMANDS:
sc.sendBroadcast(message)
}
s.clientsLock.Unlock()
case bgammon.CommandDefcon:
if len(params) == 0 {
cmd.client.sendNotice(fmt.Sprintf("Current DEFCON level: %d.", s.defcon))
continue
} else if !cmd.client.Admin() && !cmd.client.Mod() {
cmd.client.sendNotice("Access denied.")
continue
}
v, err := strconv.Atoi(string(params[0]))
if err != nil || v < 1 || v > 5 {
cmd.client.sendNotice("Failed to update DEFCON level: invalid level.")
continue
} else if v == s.defcon {
cmd.client.sendNotice("Failed to update DEFCON level: already at specified DEFCON level.")
continue
}
lastDefcon := s.defcon
s.defcon = v
cmd.client.sendNotice(fmt.Sprintf("Updated DEFCON level to %d.", v))
if lastDefcon == 5 {
s.clientsLock.Lock()
for _, sc := range s.clients {
sc.sendDefconWarning(s.defcon)
}
s.clientsLock.Unlock()
}
case bgammon.CommandShutdown:
if !cmd.client.Admin() {
cmd.client.sendNotice("Access denied.")