From 55dffb624c5f1c3a90043af2283f378b9a8a4743 Mon Sep 17 00:00:00 2001 From: jedarden Date: Thu, 30 Apr 2026 09:38:09 -0400 Subject: [PATCH] fix(worker): UsePathStyle for ARMOR and skip crash_strikes on normal game endings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- cmd/acb-worker/b2.go | 5 ++++- cmd/acb-worker/db.go | 11 ++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/cmd/acb-worker/b2.go b/cmd/acb-worker/b2.go index fea7561..4468cbd 100644 --- a/cmd/acb-worker/b2.go +++ b/cmd/acb-worker/b2.go @@ -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{ diff --git a/cmd/acb-worker/db.go b/cmd/acb-worker/db.go index a8639a2..be55269 100644 --- a/cmd/acb-worker/db.go +++ b/cmd/acb-worker/db.go @@ -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 {