diff options
Diffstat (limited to 'backend/game')
| -rw-r--r-- | backend/game/hub.go | 296 | ||||
| -rw-r--r-- | backend/game/message.go | 11 |
2 files changed, 143 insertions, 164 deletions
diff --git a/backend/game/hub.go b/backend/game/hub.go index 97303ec..7c06e05 100644 --- a/backend/game/hub.go +++ b/backend/game/hub.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "log" + "regexp" "strings" "time" @@ -86,7 +87,7 @@ func (hub *gameHub) run() { // TODO: assert game state is gaming log.Printf("submit: %v", message.message) code := msg.Data.Code - codeSize := len(code) // TODO: exclude whitespaces. + codeSize := calcCodeSize(code) codeHash := calcHash(code) if err := hub.taskQueue.EnqueueTaskCreateSubmissionRecord( hub.game.gameID, @@ -101,8 +102,7 @@ func (hub *gameHub) run() { hub.broadcastToWatchers(&watcherMessageS2CSubmit{ Type: watcherMessageTypeS2CSubmit, Data: watcherMessageS2CSubmitPayload{ - PlayerID: message.client.playerID, - PreliminaryScore: codeSize, + PlayerID: message.client.playerID, }, }) default: @@ -138,6 +138,55 @@ func (hub *gameHub) run() { } } +func (hub *gameHub) sendExecResultMessage(playerID int, testcaseID nullable.Nullable[int], status string, stdout string, stderr string) { + hub.sendToPlayer(playerID, &playerMessageS2CExecResult{ + Type: playerMessageTypeS2CExecResult, + Data: playerMessageS2CExecResultPayload{ + TestcaseID: testcaseID, + Status: api.GamePlayerMessageS2CExecResultPayloadStatus(status), + Stdout: stdout, + Stderr: stderr, + }, + }) + hub.broadcastToWatchers(&watcherMessageS2CExecResult{ + Type: watcherMessageTypeS2CExecResult, + Data: watcherMessageS2CExecResultPayload{ + PlayerID: playerID, + TestcaseID: testcaseID, + Status: api.GameWatcherMessageS2CExecResultPayloadStatus(status), + Stdout: stdout, + Stderr: stderr, + }, + }) +} + +func (hub *gameHub) sendSubmitResult(playerID int, status string, score nullable.Nullable[int]) { + hub.sendToPlayer(playerID, &playerMessageS2CSubmitResult{ + Type: playerMessageTypeS2CSubmitResult, + Data: playerMessageS2CSubmitResultPayload{ + Status: api.GamePlayerMessageS2CSubmitResultPayloadStatus(status), + Score: score, + }, + }) + hub.broadcastToWatchers(&watcherMessageS2CSubmitResult{ + Type: watcherMessageTypeS2CSubmitResult, + Data: watcherMessageS2CSubmitResultPayload{ + PlayerID: playerID, + Status: api.GameWatcherMessageS2CSubmitResultPayloadStatus(status), + Score: score, + }, + }) +} + +func (hub *gameHub) sendToPlayer(playerID int, msg playerMessageS2C) { + for player := range hub.players { + if player.playerID == playerID { + player.s2cMessages <- msg + return + } + } +} + func (hub *gameHub) broadcastToWatchers(msg watcherMessageS2C) { for watcher := range hub.watchers { watcher.s2cMessages <- msg @@ -160,100 +209,52 @@ func (hub *gameHub) processTaskResults() { case *taskqueue.TaskResultCreateSubmissionRecord: err := hub.processTaskResultCreateSubmissionRecord(taskResult) if err != nil { - for player := range hub.players { - if player.playerID != taskResult.TaskPayload.UserID() { - continue - } - player.s2cMessages <- &playerMessageS2CExecResult{ - Type: playerMessageTypeS2CExecResult, - Data: playerMessageS2CExecResultPayload{ - Score: nil, - Status: api.GamePlayerMessageS2CExecResultPayloadStatus(err.Status), - }, - } - } - hub.broadcastToWatchers(&watcherMessageS2CSubmitResult{ - Type: watcherMessageTypeS2CSubmitResult, - Data: watcherMessageS2CSubmitResultPayload{ - PlayerID: taskResult.TaskPayload.UserID(), - Status: api.GameWatcherMessageS2CSubmitResultPayloadStatus(err.Status), - }, - }) + hub.sendSubmitResult( + taskResult.TaskPayload.UserID(), + err.Status, + nullable.NewNullNullable[int](), + ) } case *taskqueue.TaskResultCompileSwiftToWasm: err := hub.processTaskResultCompileSwiftToWasm(taskResult) if err != nil { - for player := range hub.players { - if player.playerID != taskResult.TaskPayload.UserID() { - continue - } - player.s2cMessages <- &playerMessageS2CExecResult{ - Type: playerMessageTypeS2CExecResult, - Data: playerMessageS2CExecResultPayload{ - Score: nil, - Status: api.GamePlayerMessageS2CExecResultPayloadStatus(err.Status), - }, - } - } - hub.broadcastToWatchers(&watcherMessageS2CExecResult{ - Type: watcherMessageTypeS2CExecResult, - Data: watcherMessageS2CExecResultPayload{ - PlayerID: taskResult.TaskPayload.UserID(), - Status: api.GameWatcherMessageS2CExecResultPayloadStatus(err.Status), - Stdout: err.Stdout, - Stderr: err.Stderr, - }, - }) - hub.broadcastToWatchers(&watcherMessageS2CSubmitResult{ - Type: watcherMessageTypeS2CSubmitResult, - Data: watcherMessageS2CSubmitResultPayload{ - PlayerID: taskResult.TaskPayload.UserID(), - Status: api.GameWatcherMessageS2CSubmitResultPayloadStatus(err.Status), - }, - }) + hub.sendExecResultMessage( + taskResult.TaskPayload.UserID(), + nullable.NewNullNullable[int](), + err.Status, + err.Stdout, + err.Stderr, + ) + hub.sendSubmitResult( + taskResult.TaskPayload.UserID(), + err.Status, + nullable.NewNullNullable[int](), + ) } case *taskqueue.TaskResultCompileWasmToNativeExecutable: err := hub.processTaskResultCompileWasmToNativeExecutable(taskResult) if err != nil { - for player := range hub.players { - if player.playerID != taskResult.TaskPayload.UserID() { - continue - } - player.s2cMessages <- &playerMessageS2CExecResult{ - Type: playerMessageTypeS2CExecResult, - Data: playerMessageS2CExecResultPayload{ - Score: nil, - Status: api.GamePlayerMessageS2CExecResultPayloadStatus(err.Status), - }, - } - } - hub.broadcastToWatchers(&watcherMessageS2CExecResult{ - Type: watcherMessageTypeS2CExecResult, - Data: watcherMessageS2CExecResultPayload{ - PlayerID: taskResult.TaskPayload.UserID(), - Status: api.GameWatcherMessageS2CExecResultPayloadStatus(err.Status), - Stdout: err.Stdout, - Stderr: err.Stderr, - }, - }) - hub.broadcastToWatchers(&watcherMessageS2CSubmitResult{ - Type: watcherMessageTypeS2CSubmitResult, - Data: watcherMessageS2CSubmitResultPayload{ - PlayerID: taskResult.TaskPayload.UserID(), - Status: api.GameWatcherMessageS2CSubmitResultPayloadStatus(err.Status), - }, - }) + hub.sendExecResultMessage( + taskResult.TaskPayload.UserID(), + nullable.NewNullNullable[int](), + err.Status, + err.Stdout, + err.Stderr, + ) + hub.sendSubmitResult( + taskResult.TaskPayload.UserID(), + err.Status, + nullable.NewNullNullable[int](), + ) } else { - hub.broadcastToWatchers(&watcherMessageS2CExecResult{ - Type: watcherMessageTypeS2CExecResult, - Data: watcherMessageS2CExecResultPayload{ - PlayerID: taskResult.TaskPayload.UserID(), - Status: api.GameWatcherMessageS2CExecResultPayloadStatus("success"), - // TODO: inherit the command stdout/stderr. - Stdout: "Successfully compiled", - Stderr: "", - }, - }) + hub.sendExecResultMessage( + taskResult.TaskPayload.UserID(), + nullable.NewNullNullable[int](), + "success", + // TODO: inherit the command stdout/stderr. + "Successfully compiled", + "", + ) } case *taskqueue.TaskResultRunTestcase: // FIXME: error handling @@ -269,82 +270,52 @@ func (hub *gameHub) processTaskResults() { Stderr: "", }) if err != nil { - for player := range hub.players { - if player.playerID != taskResult.TaskPayload.UserID() { - continue - } - player.s2cMessages <- &playerMessageS2CExecResult{ - Type: playerMessageTypeS2CExecResult, - Data: playerMessageS2CExecResultPayload{ - Score: nil, - Status: api.GamePlayerMessageS2CExecResultPayloadStatus("internal_error"), - }, - } - } - hub.broadcastToWatchers(&watcherMessageS2CExecResult{ - Type: watcherMessageTypeS2CExecResult, - Data: watcherMessageS2CExecResultPayload{ - PlayerID: taskResult.TaskPayload.UserID(), - TestcaseID: nullable.NewNullableWithValue(int(taskResult.TaskPayload.TestcaseID)), - Status: api.GameWatcherMessageS2CExecResultPayloadStatus("internal_error"), - // TODO: inherit the command stdout/stderr? - Stdout: "", - Stderr: "internal error", - }, - }) - hub.broadcastToWatchers(&watcherMessageS2CSubmitResult{ - Type: watcherMessageTypeS2CSubmitResult, - Data: watcherMessageS2CSubmitResultPayload{ - PlayerID: taskResult.TaskPayload.UserID(), - Status: api.GameWatcherMessageS2CSubmitResultPayloadStatus("internal_error"), - }, - }) + hub.sendExecResultMessage( + taskResult.TaskPayload.UserID(), + nullable.NewNullableWithValue(int(taskResult.TaskPayload.TestcaseID)), + "internal_error", + // TODO: inherit the command stdout/stderr? + "", + "internal error", + ) + hub.sendSubmitResult( + taskResult.TaskPayload.UserID(), + "internal_error", + nullable.NewNullNullable[int](), + ) continue } - for player := range hub.players { - if player.playerID != taskResult.TaskPayload.UserID() { - continue - } - player.s2cMessages <- &playerMessageS2CExecResult{ - Type: playerMessageTypeS2CExecResult, - Data: playerMessageS2CExecResultPayload{ - Score: nil, - Status: api.GamePlayerMessageS2CExecResultPayloadStatus(aggregatedStatus), - }, - } - } if err1 != nil { - hub.broadcastToWatchers(&watcherMessageS2CExecResult{ - Type: watcherMessageTypeS2CExecResult, - Data: watcherMessageS2CExecResultPayload{ - PlayerID: taskResult.TaskPayload.UserID(), - TestcaseID: nullable.NewNullableWithValue(int(taskResult.TaskPayload.TestcaseID)), - Status: api.GameWatcherMessageS2CExecResultPayloadStatus(err1.Status), - Stdout: err1.Stdout, - Stderr: err1.Stderr, - }, - }) + hub.sendExecResultMessage( + taskResult.TaskPayload.UserID(), + nullable.NewNullableWithValue(int(taskResult.TaskPayload.TestcaseID)), + aggregatedStatus, + err1.Stdout, + err1.Stderr, + ) } else { - hub.broadcastToWatchers(&watcherMessageS2CExecResult{ - Type: watcherMessageTypeS2CExecResult, - Data: watcherMessageS2CExecResultPayload{ - PlayerID: taskResult.TaskPayload.UserID(), - TestcaseID: nullable.NewNullableWithValue(int(taskResult.TaskPayload.TestcaseID)), - Status: api.GameWatcherMessageS2CExecResultPayloadStatus("success"), - // TODO: inherit the command stdout/stderr? - Stdout: "Testcase passed", - Stderr: "", - }, - }) + hub.sendExecResultMessage( + taskResult.TaskPayload.UserID(), + nullable.NewNullableWithValue(int(taskResult.TaskPayload.TestcaseID)), + "success", + // TODO: inherit the command stdout/stderr? + "Testcase passed", + "", + ) } if aggregatedStatus != "running" { - hub.broadcastToWatchers(&watcherMessageS2CSubmitResult{ - Type: watcherMessageTypeS2CSubmitResult, - Data: watcherMessageS2CSubmitResultPayload{ - PlayerID: taskResult.TaskPayload.UserID(), - Status: api.GameWatcherMessageS2CSubmitResultPayloadStatus(aggregatedStatus), - }, - }) + var score nullable.Nullable[int] + if aggregatedStatus == "success" { + codeSize, err := hub.q.GetSubmissionCodeSizeByID(hub.ctx, int32(taskResult.TaskPayload.SubmissionID)) + if err == nil { + score = nullable.NewNullableWithValue(int(codeSize)) + } + } + hub.sendSubmitResult( + taskResult.TaskPayload.UserID(), + aggregatedStatus, + score, + ) } default: panic("unexpected task result type") @@ -538,14 +509,14 @@ func (hub *gameHub) startGame() error { player.s2cMessages <- &playerMessageS2CStart{ Type: playerMessageTypeS2CStart, Data: playerMessageS2CStartPayload{ - StartAt: int(startAt.Unix()), + StartAt: startAt.Unix(), }, } } hub.broadcastToWatchers(&watcherMessageS2CStart{ Type: watcherMessageTypeS2CStart, Data: watcherMessageS2CStartPayload{ - StartAt: int(startAt.Unix()), + StartAt: startAt.Unix(), }, }) err := hub.q.UpdateGameStartedAt(hub.ctx, db.UpdateGameStartedAtParams{ @@ -693,3 +664,8 @@ func isTestcaseResultCorrect(expectedStdout, actualStdout string) bool { func calcHash(code string) string { return fmt.Sprintf("%x", md5.Sum([]byte(code))) } + +func calcCodeSize(code string) int { + re := regexp.MustCompile(`\s+`) + return len(re.ReplaceAllString(code, "")) +} diff --git a/backend/game/message.go b/backend/game/message.go index 4877ac4..b535c1d 100644 --- a/backend/game/message.go +++ b/backend/game/message.go @@ -8,10 +8,11 @@ import ( ) const ( - playerMessageTypeS2CStart = "player:s2c:start" - playerMessageTypeS2CExecResult = "player:s2c:execresult" - playerMessageTypeC2SCode = "player:c2s:code" - playerMessageTypeC2SSubmit = "player:c2s:submit" + playerMessageTypeS2CStart = "player:s2c:start" + playerMessageTypeS2CExecResult = "player:s2c:execresult" + playerMessageTypeS2CSubmitResult = "player:s2c:submitresult" + playerMessageTypeC2SCode = "player:c2s:code" + playerMessageTypeC2SSubmit = "player:c2s:submit" ) type playerMessageC2SWithClient struct { @@ -24,6 +25,8 @@ type playerMessageS2CStart = api.GamePlayerMessageS2CStart type playerMessageS2CStartPayload = api.GamePlayerMessageS2CStartPayload type playerMessageS2CExecResult = api.GamePlayerMessageS2CExecResult type playerMessageS2CExecResultPayload = api.GamePlayerMessageS2CExecResultPayload +type playerMessageS2CSubmitResult = api.GamePlayerMessageS2CSubmitResult +type playerMessageS2CSubmitResultPayload = api.GamePlayerMessageS2CSubmitResultPayload type playerMessageC2S = interface{} type playerMessageC2SCode = api.GamePlayerMessageC2SCode |
