fix(worker): UsePathStyle for ARMOR and skip crash_strikes on normal game endings

Two fixes:
1. Add UsePathStyle=true to B2 S3 client. Without it the SDK uses
   virtual-hosted addressing, dropping the bucket name from the request
   path. Uploads hit /replays/... instead of /armor-apexalgo/replays/...
   causing NoSuchBucket errors on every replay/thumbnail PutObject.

2. Don't update crash_strikes for normal game endings (stalemate, turns).
   In snake-style games every bot eventually crashes into a wall/snake —
   that is the expected end condition, not an HTTP error. The old code
   treated all Crashed[] entries from the engine as errors, causing all
   6 bots to accumulate strikes after every single match and hitting the
   30-min cooldown threshold after just 3 matches.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jedarden 2026-04-30 09:38:09 -04:00
parent ae0f072f80
commit 55dffb624c
2 changed files with 12 additions and 4 deletions

View file

@ -45,11 +45,14 @@ func NewB2Client(cfg *Config) *B2Client {
}
// Create S3 client with custom endpoint (ARMOR proxy wrapping B2)
// Use custom EndpointResolverV2 to bypass endpoint rules entirely
// UsePathStyle is required: without it the SDK uses virtual-hosted style and
// drops the bucket name from the URL path (bucket ends up in hostname which
// the custom resolver replaces, losing it entirely).
client := s3.NewFromConfig(awsCfg, func(o *s3.Options) {
o.EndpointResolverV2 = &customEndpointResolver{
endpointURL: *endpointURL,
}
o.UsePathStyle = true
})
return &B2Client{

View file

@ -399,9 +399,14 @@ func (c *DBClient) SubmitMatchResult(ctx context.Context, jobID string, result *
log.Printf("failed to update series result for match %s: %v", matchID, err)
}
// Update crash strikes and cooldown for each participant
if err := updateCrashStrikes(ctx, tx, result.CrashedBots); err != nil {
log.Printf("failed to update crash strikes for match %s: %v", matchID, err)
// Update crash strikes only for technical failures, not for normal game
// endings. In snake-style games every bot eventually "crashes" (hits a wall
// or another snake) — that is the normal end condition, not an error.
// "stalemate" and "turns" are the expected EndReason values for normal play.
if result.EndReason != "stalemate" && result.EndReason != "turns" {
if err := updateCrashStrikes(ctx, tx, result.CrashedBots); err != nil {
log.Printf("failed to update crash strikes for match %s: %v", matchID, err)
}
}
if err := tx.Commit(); err != nil {