diff --git a/client/router.go b/client/router.go index 4ef4a95..9aa2e1a 100644 --- a/client/router.go +++ b/client/router.go @@ -4,6 +4,7 @@ import ( "log/slog" "time" + "github.com/getsentry/sentry-go" "github.com/hajimehoshi/ebiten/v2" "github.com/xescugc/go-flux" "github.com/xescugc/maze-wars/action" @@ -53,6 +54,32 @@ func (rs *RouterStore) Update() error { b := time.Now() defer utils.LogTime(rs.logger, b, "router update") + // Clone the current hub so that modifications of the scope are visible only + // within this function. + hub := sentry.CurrentHub().Clone() + + // See https://golang.org/ref/spec#Handling_panics. + // This will recover from runtime panics and then panic again after + // reporting to Sentry. + defer func() { + if x := recover(); x != nil { + // Create an event and enqueue it for reporting. + hub.Recover(x) + // Because the goroutine running this code is going to crash the + // program, call Flush to send the event to Sentry before it is too + // late. Set the timeout to an appropriate value depending on your + // program. The value is the maximum time to wait before giving up + // and dropping the event. + hub.Flush(2 * time.Second) + // Note that if multiple goroutines panic, possibly only the first + // one to call Flush will succeed in sending the event. If you want + // to capture multiple panics and still crash the program + // afterwards, you need to coordinate error reporting and + // termination differently. + panic(x) + } + }() + rstate := rs.GetState().(RouterState) switch rstate.Route { case utils.SignUpRoute: @@ -77,6 +104,32 @@ func (rs *RouterStore) Draw(screen *ebiten.Image) { b := time.Now() defer utils.LogTime(rs.logger, b, "router draw") + // Clone the current hub so that modifications of the scope are visible only + // within this function. + hub := sentry.CurrentHub().Clone() + + // See https://golang.org/ref/spec#Handling_panics. + // This will recover from runtime panics and then panic again after + // reporting to Sentry. + defer func() { + if x := recover(); x != nil { + // Create an event and enqueue it for reporting. + hub.Recover(x) + // Because the goroutine running this code is going to crash the + // program, call Flush to send the event to Sentry before it is too + // late. Set the timeout to an appropriate value depending on your + // program. The value is the maximum time to wait before giving up + // and dropping the event. + hub.Flush(2 * time.Second) + // Note that if multiple goroutines panic, possibly only the first + // one to call Flush will succeed in sending the event. If you want + // to capture multiple panics and still crash the program + // afterwards, you need to coordinate error reporting and + // termination differently. + panic(x) + } + }() + rstate := rs.GetState().(RouterState) switch rstate.Route { case utils.SignUpRoute: diff --git a/cmd/client/main.go b/cmd/client/main.go index 2cc2fd7..87c8901 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -3,11 +3,14 @@ package main import ( "context" "fmt" + "log" "log/slog" "os" "path" + "time" "github.com/adrg/xdg" + "github.com/getsentry/sentry-go" "github.com/spf13/cobra" "github.com/xescugc/go-flux" "github.com/xescugc/maze-wars/client" @@ -114,9 +117,23 @@ func init() { } func main() { - if err := clientCmd.Execute(); err != nil { + err := sentry.Init(sentry.ClientOptions{ + // Either set your DSN here or set the SENTRY_DSN environment variable. + Dsn: "https://23c84ec9b6be647cd894cef01d883bb2@o4507290827751424.ingest.de.sentry.io/4507293420617808", + // Enable printing of SDK debug messages. + // Useful when getting started or trying to figure something out. + EnableTracing: true, + Release: version, + }) + if err != nil { + log.Fatalf("sentry.Init: %s", err) + } + // Flush buffered events before the program terminates. + // Set the timeout to the maximum duration the program can afford to wait. + defer sentry.Flush(2 * time.Second) + + if err = clientCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) - } } diff --git a/cmd/server/main.go b/cmd/server/main.go index 7b296a9..d2fb806 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -2,12 +2,15 @@ package main import ( "fmt" + "log" "log/slog" "os" "path" "strings" + "time" "github.com/adrg/xdg" + "github.com/getsentry/sentry-go" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/xescugc/go-flux" @@ -71,6 +74,21 @@ func init() { } func main() { + err := sentry.Init(sentry.ClientOptions{ + // Either set your DSN here or set the SENTRY_DSN environment variable. + Dsn: "https://23c84ec9b6be647cd894cef01d883bb2@o4507290827751424.ingest.de.sentry.io/4507293420617808", + // Enable printing of SDK debug messages. + // Useful when getting started or trying to figure something out. + EnableTracing: true, + Release: version, + }) + if err != nil { + log.Fatalf("sentry.Init: %s", err) + } + // Flush buffered events before the program terminates. + // Set the timeout to the maximum duration the program can afford to wait. + defer sentry.Flush(2 * time.Second) + if err := serverCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) diff --git a/cmd/server/version.go b/cmd/server/version.go index edb2b17..287b725 100644 --- a/cmd/server/version.go +++ b/cmd/server/version.go @@ -9,7 +9,7 @@ import ( var ( // version is the value of the current version, this // is set via -ldflags - version string + version string = "development" versionCmd = &cobra.Command{ Use: "version", diff --git a/go.mod b/go.mod index 25bcf64..85f3747 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.21 require ( github.com/adrg/xdg v0.4.0 github.com/ebitenui/ebitenui v0.5.6-0.20240228194824-a73d28dc4078 + github.com/getsentry/sentry-go v0.27.0 github.com/gofrs/uuid v4.4.0+incompatible github.com/golang/mock v1.6.0 github.com/gorilla/handlers v1.5.2 diff --git a/go.sum b/go.sum index 097d6ae..22f0c5b 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,10 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= @@ -56,6 +60,10 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/server/assets/wasm/maze-wars.wasm b/server/assets/wasm/maze-wars.wasm index afe6e15..57bc29c 100755 Binary files a/server/assets/wasm/maze-wars.wasm and b/server/assets/wasm/maze-wars.wasm differ diff --git a/server/new.go b/server/new.go index 46d8121..2586d8e 100644 --- a/server/new.go +++ b/server/new.go @@ -18,6 +18,9 @@ import ( "github.com/xescugc/maze-wars/server/assets" "github.com/xescugc/maze-wars/server/models" "github.com/xescugc/maze-wars/server/templates" + + "github.com/getsentry/sentry-go" + sentryhttp "github.com/getsentry/sentry-go/http" ) var ( @@ -34,6 +37,18 @@ func New(ad *ActionDispatcher, s *Store, opt Options) error { go startLoop(ctx, s) + // To initialize Sentry's handler, you need to initialize Sentry itself beforehand + if err := sentry.Init(sentry.ClientOptions{ + Dsn: "https://23c84ec9b6be647cd894cef01d883bb2@o4507290827751424.ingest.de.sentry.io/4507293420617808", + // Set TracesSampleRate to 1.0 to capture 100% + // of transactions for performance monitoring. + // We recommend adjusting this value in production, + TracesSampleRate: 1.0, + EnableTracing: true, + }); err != nil { + return fmt.Errorf("Sentry initialization failed: %v\n", err) + } + r := mux.NewRouter() // Game Websocket @@ -59,9 +74,11 @@ func New(ad *ActionDispatcher, s *Store, opt Options) error { hmux.Handle("/wasm/", http.FileServer(http.FS(assets.Assets))) hmux.Handle("/images/", http.FileServer(http.FS(assets.Assets))) + handler := sentryhttp.New(sentryhttp.Options{}).Handle(hmux) + svr := &http.Server{ Addr: fmt.Sprintf(":%s", opt.Port), - Handler: handlers.LoggingHandler(os.Stdout, hmux), + Handler: handlers.LoggingHandler(os.Stdout, handler), } log.Printf("Staring server at %s\n", opt.Port) @@ -235,6 +252,31 @@ func wsHandler(s *Store) func(http.ResponseWriter, *http.Request) { } func startLoop(ctx context.Context, s *Store) { + // Clone the current hub so that modifications of the scope are visible only + // within this function. + hub := sentry.CurrentHub().Clone() + + // See https://golang.org/ref/spec#Handling_panics. + // This will recover from runtime panics and then panic again after + // reporting to Sentry. + defer func() { + if x := recover(); x != nil { + // Create an event and enqueue it for reporting. + hub.Recover(x) + // Because the goroutine running this code is going to crash the + // program, call Flush to send the event to Sentry before it is too + // late. Set the timeout to an appropriate value depending on your + // program. The value is the maximum time to wait before giving up + // and dropping the event. + hub.Flush(2 * time.Second) + // Note that if multiple goroutines panic, possibly only the first + // one to call Flush will succeed in sending the event. If you want + // to capture multiple panics and still crash the program + // afterwards, you need to coordinate error reporting and + // termination differently. + panic(x) + } + }() secondTicker := time.NewTicker(time.Second) stateTicker := time.NewTicker(time.Second / 4) // The default TPS on of Ebiten client if 60 so to diff --git a/server/templates/views/game/play.tmpl b/server/templates/views/game/play.tmpl index a830438..8e3704d 100644 --- a/server/templates/views/game/play.tmpl +++ b/server/templates/views/game/play.tmpl @@ -3,6 +3,10 @@ Maze Wars - Play {{ end }} {{define "content"}} +

The current web version of this game is really slow. If you want a smooth experience download it.