containers-starter-kit/SdkGoWrapper/server.go (129 lines of code) (raw):

package main import ( "aws/amazon-gamelift-go-sdk/model" "aws/amazon-gamelift-go-sdk/server" "log" "time" "os" "os/signal" "syscall" "fmt" "strconv" "net/http" "encoding/json" ) // GameSession data var GameSession model.GameSession // UpdateGameSession data var UpdateGameSession model.UpdateGameSession type gameProcess struct { // Port - port for incoming player connections Port int // Logs - set of files to upload when the game session ends. We don't use this with container-based deployments Logs server.LogParameters } func (g gameProcess) OnStartGameSession(session model.GameSession) { // Store the session in the global variable GameSession = session // Activate the game session when it's created err := server.ActivateGameSession() if err != nil { log.Fatal(err.Error()) } log.Print("Activated game session") } func (g gameProcess) OnUpdateGameSession(session model.UpdateGameSession) { // Handle updated game session data (FlexMatch backfilling) // Store the update data in the global variable UpdateGameSession = session } func (g gameProcess) OnProcessTerminate() { // Amazon GameLift will invoke this callback before shutting down an instance hosting this game server. // This will not happen on instances running game sessions and with fleet instance protection on, unless they are running on Spot and the instance is interrupted // First we tell GameLift we're ending the process server.ProcessEnding() // Send a termination signal to the parent process which is the wrapper.sh (this will terminate both this process, as well as the game server) // Get parent process ID ppid := os.Getppid() fmt.Printf("Parent Process ID: %d\n", ppid) // Get handle to parent process parent, err := os.FindProcess(ppid) if err != nil { fmt.Printf("Failed to find parent process: %v\n", err) return } // Send SIGKILL signal to parent fmt.Printf("Attempting to terminate parent process %d\n", ppid) err = parent.Signal(syscall.SIGKILL) if err != nil { fmt.Printf("Failed to terminate parent process: %v\n", err) return } // Terminate the wrapper (this should already happen when we terminate the parent process) os.Exit(0) } func (g gameProcess) OnHealthCheck() bool { // We expect the game server to be healthy as long as it's running. For deep health checks, integrate the SDK to the game server itself. return true } // A HTTP server to provide game session data to the game server process on request. Only runs on localhost for security func HttpServer(port int) { go func() { http.HandleFunc("/gamesessiondata", GameSessionData) http.ListenAndServe("127.0.0.1:"+strconv.Itoa(port), nil) }() } func GameSessionData(w http.ResponseWriter, req *http.Request) { // If UpdateGameSessionData GameSessionId is not empty, return the updated data // NOTE: We're not returning the UpdateReason or BackfillTicketID that are also included in an UpdateGameSession, just the game session data itself if UpdateGameSession.GameSession.GameSessionID != "" { log.Print("Returning Updated GameSessionData") jsonBytes, err := json.Marshal(UpdateGameSession.GameSession) if err != nil { fmt.Println("Error:", err) // Set the response status code to 500 (Internal Server Error) w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "error\n") return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) return } // else try to return the initial game session data (could be empty values but that's fine, client can handle that) log.Print("Returning GameSessionData") jsonBytes, err := json.Marshal(GameSession) if err != nil { fmt.Println("Error:", err) // Set the response status code to 500 (Internal Server Error) w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "error\n") return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) } func main() { // Start the localhost server thread to provide information to the game server process on request HttpServer(8090) // Exit if no port is specified if len(os.Args) < 2 { fmt.Println("No port specified") os.Exit(1) } // Get the port on cli args log.Print("Getting the game server port") port, err := strconv.Atoi(os.Args[1]) if err != nil { panic(err) } log.Print("Game server port is: ", port) log.Print("Starting GameLift wrapper") // Initialize the Amazon GameLift Server SDK err2 := server.InitSDK(server.ServerParameters{}) if err2 != nil { log.Fatal(err2.Error()) } // Make sure to call server.ProcessEnding() when the application quits. // This tells GameLift the session has ended defer server.ProcessEnding() process := gameProcess{ Port: port, Logs: server.LogParameters{ // The log path is not actually used with container fleets... LogPaths: []string{"/local/game/logs/myserver.log"}, }, } // Register our process to the Amazon GameLift service err = server.ProcessReady(server.ProcessParameters{ OnStartGameSession: process.OnStartGameSession, OnUpdateGameSession: process.OnUpdateGameSession, OnProcessTerminate: process.OnProcessTerminate, OnHealthCheck: process.OnHealthCheck, LogParameters: process.Logs, Port: process.Port, }) if err != nil { log.Fatal(err.Error()) } // Create channel for SIGINT sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, os.Interrupt, syscall.SIGINT) // Wait until we get the interruption signal for { select { case <-sigChan: log.Print("Received SIGINT, shutting down...") return default: time.Sleep(50 * time.Millisecond) } } }