parent
7930427638
commit
85e092b8cb
6 changed files with 121 additions and 6 deletions
|
@ -123,6 +123,14 @@ must write some data to the server at least once every 40 seconds.
|
|||
- `disconnect`
|
||||
- Disconnect from the server.
|
||||
|
||||
- `broadcast`
|
||||
- Send a message to all players.
|
||||
- This command is only available to server administrators.
|
||||
|
||||
- `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.
|
||||
|
||||
## Server events
|
||||
|
||||
All events are sent in either JSON or human-readable format. The structure of
|
||||
|
|
|
@ -29,6 +29,8 @@ const (
|
|||
CommandBoard = "board" // Print current board state in human-readable form.
|
||||
CommandPong = "pong" // Response to server ping.
|
||||
CommandDisconnect = "disconnect" // Disconnect from server.
|
||||
CommandBroadcast = "broadcast" // Send a message to all players.
|
||||
CommandShutdown = "shutdown" // Prevent the creation of new matches.
|
||||
)
|
||||
|
||||
type EventType string
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"time"
|
||||
|
||||
"code.rocket9labs.com/tslocum/bgammon"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
)
|
||||
|
||||
type clientRating struct {
|
||||
|
@ -84,6 +85,10 @@ type serverClient struct {
|
|||
bgammon.Client
|
||||
}
|
||||
|
||||
func (c *serverClient) Admin() bool {
|
||||
return c.accountID == 1
|
||||
}
|
||||
|
||||
func (c *serverClient) sendEvent(e interface{}) {
|
||||
// JSON formatted messages.
|
||||
if c.json {
|
||||
|
@ -205,6 +210,12 @@ func (c *serverClient) sendNotice(message string) {
|
|||
})
|
||||
}
|
||||
|
||||
func (c *serverClient) sendBroadcast(message string) {
|
||||
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)
|
||||
|
|
|
@ -55,6 +55,9 @@ msgstr ""
|
|||
msgid "Failed to create match: Please leave the match you are in before creating another."
|
||||
msgstr ""
|
||||
|
||||
msgid "Failed to create match: The server is shutting down. Reason: %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "Failed to log in: %s"
|
||||
msgstr ""
|
||||
|
||||
|
@ -109,6 +112,9 @@ msgstr ""
|
|||
msgid "Please enter an email, username and password."
|
||||
msgstr ""
|
||||
|
||||
msgid "Please finish your match as soon as possible."
|
||||
msgstr ""
|
||||
|
||||
msgid "Please leave the match you are in before joining another."
|
||||
msgstr ""
|
||||
|
||||
|
@ -121,6 +127,9 @@ msgstr ""
|
|||
msgid "Resigned."
|
||||
msgstr ""
|
||||
|
||||
msgid "SERVER BROADCAST:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Server error"
|
||||
msgstr ""
|
||||
|
||||
|
@ -133,6 +142,15 @@ msgstr ""
|
|||
msgid "The match you are in is still in progress."
|
||||
msgstr ""
|
||||
|
||||
msgid "The server is shutting down. Reason:"
|
||||
msgstr ""
|
||||
|
||||
msgid "The server is shutting down in 1 minute. Reason:"
|
||||
msgstr ""
|
||||
|
||||
msgid "The server is shutting down in %d minutes. Reason:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unknown command: %s"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -86,6 +86,9 @@ type server struct {
|
|||
|
||||
relayChat bool // Chats are not relayed normally. This option is only used by local servers.
|
||||
verbose bool
|
||||
|
||||
shutdownTime time.Time
|
||||
shutdownReason string
|
||||
}
|
||||
|
||||
func NewServer(tz string, dataSource string, mailServer string, passwordSalt string, resetSalt string, relayChat bool, verbose bool, allowDebug bool) *server {
|
||||
|
@ -539,6 +542,44 @@ func (s *server) Analyze(g *bgammon.Game) {
|
|||
os.Exit(0)
|
||||
}
|
||||
|
||||
func (s *server) handleShutdown() {
|
||||
var mins time.Duration
|
||||
var minutes int
|
||||
t := time.NewTicker(time.Minute)
|
||||
for {
|
||||
mins = time.Until(s.shutdownTime)
|
||||
if mins > 0 {
|
||||
minutes = int(mins.Minutes()) + 1
|
||||
}
|
||||
|
||||
s.clientsLock.Lock()
|
||||
for _, sc := range s.clients {
|
||||
switch minutes {
|
||||
case 0:
|
||||
sc.sendBroadcast(gotext.GetD(sc.language, "The server is shutting down. Reason:"))
|
||||
case 1:
|
||||
sc.sendBroadcast(gotext.GetD(sc.language, "The server is shutting down in 1 minute. Reason:"))
|
||||
default:
|
||||
sc.sendBroadcast(gotext.GetD(sc.language, "The server is shutting down in %d minutes. Reason:", minutes))
|
||||
}
|
||||
sc.sendBroadcast(s.shutdownReason)
|
||||
sc.sendBroadcast(gotext.GetD(sc.language, "Please finish your match as soon as possible."))
|
||||
}
|
||||
s.clientsLock.Unlock()
|
||||
|
||||
<-t.C
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) shutdown(delay time.Duration, reason string) {
|
||||
if !s.shutdownTime.IsZero() {
|
||||
return
|
||||
}
|
||||
s.shutdownTime = time.Now().Add(delay)
|
||||
s.shutdownReason = reason
|
||||
go s.handleShutdown()
|
||||
}
|
||||
|
||||
func RandInt(max int) int {
|
||||
i, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
|
||||
if err != nil {
|
||||
|
|
|
@ -245,7 +245,7 @@ COMMANDS:
|
|||
clientGame := s.gameByClient(cmd.client)
|
||||
if clientGame != nil && clientGame.client1 != cmd.client && clientGame.client2 != cmd.client {
|
||||
switch keyword {
|
||||
case bgammon.CommandHelp, "h", bgammon.CommandJSON, bgammon.CommandList, "ls", bgammon.CommandBoard, "b", bgammon.CommandLeave, "l", bgammon.CommandReplay, bgammon.CommandSet, bgammon.CommandDisconnect, bgammon.CommandPong:
|
||||
case bgammon.CommandHelp, "h", bgammon.CommandJSON, bgammon.CommandList, "ls", bgammon.CommandBoard, "b", bgammon.CommandLeave, "l", bgammon.CommandReplay, bgammon.CommandSet, bgammon.CommandPong, bgammon.CommandDisconnect, bgammon.CommandBroadcast, bgammon.CommandShutdown:
|
||||
// These commands are allowed to be used by spectators.
|
||||
default:
|
||||
cmd.client.sendNotice(gotext.GetD(cmd.client.language, "Command ignored: You are spectating this match."))
|
||||
|
@ -320,6 +320,9 @@ COMMANDS:
|
|||
if clientGame != nil {
|
||||
cmd.client.sendNotice(gotext.GetD(cmd.client.language, "Failed to create match: Please leave the match you are in before creating another."))
|
||||
continue
|
||||
} else if !s.shutdownTime.IsZero() {
|
||||
cmd.client.sendNotice(gotext.GetD(cmd.client.language, "Failed to create match: The server is shutting down. Reason: %s", s.shutdownReason))
|
||||
continue
|
||||
}
|
||||
|
||||
sendUsage := func() {
|
||||
|
@ -1232,20 +1235,52 @@ COMMANDS:
|
|||
ev.CasualTabulaMulti = a.casual.tabulaMulti / 100
|
||||
}
|
||||
cmd.client.sendEvent(ev)
|
||||
case bgammon.CommandPong:
|
||||
// Do nothing.
|
||||
case bgammon.CommandDisconnect:
|
||||
if clientGame != nil {
|
||||
clientGame.removeClient(cmd.client)
|
||||
}
|
||||
cmd.client.Terminate("Client disconnected")
|
||||
case bgammon.CommandPong:
|
||||
// Do nothing.
|
||||
case bgammon.CommandBroadcast:
|
||||
if !cmd.client.Admin() {
|
||||
cmd.client.sendNotice("Access denied.")
|
||||
continue
|
||||
} else if len(params) == 0 {
|
||||
cmd.client.sendNotice("Please specify a message to broadcast.")
|
||||
continue
|
||||
}
|
||||
|
||||
message := string(bytes.Join(params, []byte(" ")))
|
||||
s.clientsLock.Lock()
|
||||
for _, sc := range s.clients {
|
||||
sc.sendBroadcast(message)
|
||||
}
|
||||
s.clientsLock.Unlock()
|
||||
case bgammon.CommandShutdown:
|
||||
if !cmd.client.Admin() {
|
||||
cmd.client.sendNotice("Access denied.")
|
||||
continue
|
||||
} else if len(params) < 2 {
|
||||
cmd.client.sendNotice("Please specify the number of minutes until shutdown and the reason.")
|
||||
continue
|
||||
} else if !s.shutdownTime.IsZero() {
|
||||
cmd.client.sendNotice("Server shutdown already in progress.")
|
||||
continue
|
||||
}
|
||||
|
||||
minutes, err := strconv.Atoi(string(params[0]))
|
||||
if err != nil || minutes <= 0 {
|
||||
cmd.client.sendNotice("Error: Invalid shutdown delay.")
|
||||
continue
|
||||
}
|
||||
|
||||
s.shutdown(time.Duration(minutes)*time.Minute, string(bytes.Join(params[1:], []byte(" "))))
|
||||
case "endgame":
|
||||
if !allowDebugCommands {
|
||||
cmd.client.sendNotice(gotext.GetD(cmd.client.language, "You are not allowed to use that command."))
|
||||
continue
|
||||
}
|
||||
|
||||
if clientGame == nil {
|
||||
} else if clientGame == nil {
|
||||
cmd.client.sendNotice(gotext.GetD(cmd.client.language, "You are not currently in a match."))
|
||||
continue
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue