Duels turn Sealearn’s solo practice loop into a head-to-head competition. Two students race through the same set of adaptive questions simultaneously — every answer is scored in real time, progress is broadcast to the opponent, and a winner is declared the moment both players finish. The entire system runs over a persistent Socket.IO connection, with Redis storing ephemeral invite and duel state between events.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/DerBasilisk/SEA-ServicioEvaluaconAsistida/llms.txt
Use this file to discover all available pages before exploring further.
Architecture Overview
The duel server (duel.socket.js) mounts on the main HTTP server and registers an authentication middleware that verifies a JWT on every socket handshake:
user:<userId> so that targeted events can be delivered even when a duel room has not yet been created. Duel state is persisted in two places:
- Redis — invite records (keyed by UUID), active duel state, and
pending_duel:<userId>entries for reconnecting players - MongoDB — a permanent
Dueldocument created at acceptance and updated at completion or abandonment
In-Game Modifiers
Modifiers are one-shot powerups that a player can activate during an active duel to disrupt their opponent. Theduel:use_modifier event targets a specific player by targetId; the modifier is pushed onto duel.players[targetId].modifiers in Redis and emitted to the target via duel:modifier_received.
| Modifier ID | Label | Icon | Effect |
|---|---|---|---|
extra_questions | Preguntas extra | ➕ | Adds 3 questions to the opponent’s session |
reduced_time | Tiempo reducido | ⏱️ | Limits the opponent to 10 seconds per question |
blackout | Pantalla oscura | 🌑 | Shows a 3-second black screen to the opponent |
Duel Lifecycle
Send Invite — duel:invite
The challenger emits
duel:invite with { friendId, lessonId, conversationId }. The server generates a UUID, stores the invite in Redis, and forwards a duel:invited event to the recipient’s private room. The challenger receives duel:invite_sent with the inviteId.Receive Invite — duel:invited
The recipient’s client receives
duel:invited containing { inviteId, lessonId, requesterId, conversationId }. The UI typically shows a modal or notification giving the recipient the option to accept or decline. The invite is stored in Redis with a TTL and will expire automatically if no response arrives in time.Accept or Reject — duel:accept / duel:reject
If the recipient emits
duel:accept with { inviteId }, the server:- Fetches and deletes the invite from Redis
- Loads the lesson and runs
getAdaptiveConfig()+selectQuestions()using the challenger’s user ID - Sanitises the questions (strips
correctBoolean,correctAnswers, shuffles options) - Creates a
duelStateobject in Redis and a permanentDueldocument in MongoDB - Both players are joined to the
duel:<duelId>room
duel:reject with { inviteId }, the invite is deleted from Redis and duel:rejected is sent back to the challenger.Duel Starts — duel:start
Both players receive
duel:start with { duelId, questions, opponentId }. If the challenger is temporarily disconnected when the duel is created, the payload is stored in Redis as pending_duel:<userId> (TTL 120 seconds) and delivered the next time that socket reconnects.Answer Questions — duel:answer
Players emit The server evaluates correctness (supporting
duel:answer for each question:multiple_choice, true_false, fill_blank, and order_items in duel mode), updates the player’s score, correct, and currentIndex in Redis, and emits two events:duel:answer_result→ to the answering player:{ isCorrect, explanation, correctAnswer }duel:opponent_progress→ to the other player:{ userId, currentIndex, score, correct, finished }
Duel Ends — duel:finished
When both players have Results are also stored in Redis as
finished: true, endDuel() is called. The winner is determined first by most correct answers, then by earliest finish time (finishedAt). The result is broadcast to duel:<duelId> as duel:finished:duel_result:<userId> (TTL 300 seconds) for late-arriving clients, and a duel_result message is posted to the linked chat conversation (if conversationId was provided).Duel Statistics
Every completed or abandoned duel updates the user’s duel statistics on theUser model:
addLeagueXP.