Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update the simple game server to move itself back to the Ready state after allocation #2409

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/simple-game-server/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ WITH_WINDOWS=1

mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
project_path := $(dir $(mkfile_path))
server_tag = $(REGISTRY)/simple-game-server:0.5
server_tag = $(REGISTRY)/simple-game-server:0.6
ifeq ($(WITH_WINDOWS), 1)
server_tag_linux_amd64 = $(server_tag)-linux_amd64
else
Expand Down
20 changes: 11 additions & 9 deletions examples/simple-game-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ There are some text commands you can send the server to affect its behavior:
The server has a few configuration options that can be set via command line
flags. Some can also be set using environment variables.

| Flag | Environment Variable | Default |
| ------------------------- | -------------------- | ------- |
| port | PORT | 7654 |
| passthrough | PASSTHROUGH | false |
| ready | READY | true |
| automaticShutdownDelayMin | _n/a_ | 0 |
| readyDelaySec | _n/a_ | 0 |
| udp | UDP | true |
| tcp | TCP | false |
| Flag | Environment Variable | Default |
| -------------------------------------- | -------------------- | ------- |
| port | PORT | 7654 |
| passthrough | PASSTHROUGH | false |
| ready | READY | true |
| automaticShutdownDelaySec | _n/a_ | 0 |
| automaticShutdownDelayMin (deprecated) | _n/a_ | 0 |
| readyDelaySec | _n/a_ | 0 |
| readyIterations | _n/a_ | 0 |
ilkercelikyilmaz marked this conversation as resolved.
Show resolved Hide resolved
| udp | UDP | true |
| tcp | TCP | false |

77 changes: 54 additions & 23 deletions examples/simple-game-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ func main() {
readyOnStart := flag.Bool("ready", true, "Mark this GameServer as Ready on startup")
shutdownDelayMin := flag.Int("automaticShutdownDelayMin", 0, "[Deprecated] If greater than zero, automatically shut down the server this many minutes after the server becomes allocated (please use automaticShutdownDelaySec instead)")
shutdownDelaySec := flag.Int("automaticShutdownDelaySec", 0, "If greater than zero, automatically shut down the server this many seconds after the server becomes allocated (cannot be used if automaticShutdownDelayMin is set)")
readyDelaySec := flag.Int("readyDelaySec", 0, "If greater than zero and readyOnStart is true, wait this many seconds before marking the game server as ready")
readyDelaySec := flag.Int("readyDelaySec", 0, "If greater than zero, wait this many seconds each time before marking the game server as ready")
readyIterations := flag.Int("readyIterations", 0, "If greater than zero, return to a ready state this number of times before shutting down")
udp := flag.Bool("udp", true, "Server will listen on UDP")
tcp := flag.Bool("tcp", false, "Server will listen on TCP")

Expand All @@ -66,8 +67,12 @@ func main() {
tcp = &t
}

if !*udp && !*tcp {
log.Fatalf("No protocol enabled, exiting")
// Check for incompatible flags.
if *shutdownDelayMin > 0 && *shutdownDelaySec > 0 {
log.Fatalf("Cannot set both --automaticShutdownDelayMin and --automaticShutdownDelaySec")
}
if *readyIterations > 0 && *shutdownDelayMin <= 0 && *shutdownDelaySec <= 0 {
log.Fatalf("Must set a shutdown delay if using ready iterations")
}

log.Print("Creating SDK instance")
Expand Down Expand Up @@ -108,15 +113,10 @@ func main() {
ready(s)
}


if *shutdownDelayMin > 0 && *shutdownDelaySec > 0 {
log.Fatalf("Cannot set both --automaticShutdownDelayMin and --automaticShutdownDelaySec")
}
if *shutdownDelayMin > 0 {
shutdownAfterAllocation(s, *shutdownDelayMin * 60)
}
if *shutdownDelaySec > 0 {
shutdownAfterAllocation(s, *shutdownDelaySec)
shutdownAfterNAllocations(s, *readyIterations, *shutdownDelaySec)
} else if *shutdownDelayMin > 0 {
shutdownAfterNAllocations(s, *readyIterations, *shutdownDelayMin * 60)
}

// Prevent the program from quitting as the server is listening on goroutines.
Expand All @@ -131,20 +131,51 @@ func doSignal() {
os.Exit(0)
}

// shutdownAfterAllocation creates a callback to automatically shut down
// shutdownAfterNAllocations creates a callback to automatically shut down
// the server a specified number of seconds after the server becomes
// allocated.
func shutdownAfterAllocation(s *sdk.SDK, shutdownDelay int) {
err := s.WatchGameServer(func(gs *coresdk.GameServer) {
if gs.Status.State == "Allocated" {
time.Sleep(time.Duration(shutdownDelay) * time.Second)
shutdownErr := s.Shutdown()
if shutdownErr != nil {
log.Fatalf("Could not shutdown: %v", shutdownErr)
}
}
})
// allocated the Nth time.
//
// The algorithm is:
//
// 1. Move the game server back to ready N times after it is allocated
// 2. Shutdown the game server after the Nth time is becomes allocated
//
// This follows the integration pattern documented on the website at
// https://agones.dev/site/docs/integration-patterns/reusing-gameservers/
func shutdownAfterNAllocations(s *sdk.SDK, readyIterations, shutdownDelaySec int) {
gs, err := s.GameServer()
if err != nil {
log.Fatalf("Could not get game server: %v", err)
}
currentState := gs.Status.State
if err := s.WatchGameServer(func(gs *coresdk.GameServer) {
log.Printf("Watch Game Server callback fired. State = %s", gs.Status.State)
if currentState == "Ready" && gs.Status.State == "Allocated" {
log.Println("Game Server Allocated")
readyIterations--
// Run asynchronously
go func(iterations int) {
time.Sleep(time.Duration(shutdownDelaySec) * time.Second)

if iterations > 0 {
log.Println("Moving Game Server back to Ready")
readyErr := s.Ready()
if readyErr != nil {
log.Fatalf("Could not set game server to ready: %v", readyErr)
}
return
}

log.Println("Shutting down Game Server")
shutdownErr := s.Shutdown()
if shutdownErr != nil {
log.Fatalf("Could not shutdown game server: %v", shutdownErr)
}
os.Exit(0)
}(readyIterations)
}
currentState = gs.Status.State
}); err != nil {
log.Fatalf("Could not watch Game Server events, %v", err)
}
}
Expand Down