@@ -1315,157 +1315,6 @@ const initialize = (io) => {
13151315 }
13161316 } ) ;
13171317
1318- // Handle "Play Again" for private lobbies (host only)
1319- // Creates a new lobby with the same settings and migrates all connected players
1320- socket . on ( 'lobby:playAgain' , async ( data , callback ) => {
1321- const { user : hostNetid , userId : hostUserId } = socket . userInfo ;
1322- const { code : oldCode } = data ;
1323-
1324- try {
1325- console . log ( `Host ${ hostNetid } requesting play again for lobby ${ oldCode } ` ) ;
1326- const oldRace = activeRaces . get ( oldCode ) ;
1327- const oldPlayers = racePlayers . get ( oldCode ) ;
1328-
1329- if ( ! oldRace || oldRace . type !== 'private' ) {
1330- throw new Error ( 'Lobby not found or not private.' ) ;
1331- }
1332-
1333- if ( oldRace . hostId !== hostUserId ) {
1334- throw new Error ( 'Only the host can start a new match.' ) ;
1335- }
1336-
1337- if ( oldRace . status !== 'finished' ) {
1338- throw new Error ( 'Race has not finished yet.' ) ;
1339- }
1340-
1341- // Use previous lobby settings to generate a new snippet
1342- const prevSettings = oldRace . settings || { } ;
1343- let snippetId = null ;
1344- let snippet = null ;
1345-
1346- if ( prevSettings . testMode === 'timed' && prevSettings . testDuration ) {
1347- const duration = parseInt ( prevSettings . testDuration ) || 30 ;
1348- snippet = createTimedTestSnippet ( duration ) ;
1349- } else {
1350- const { difficulty, type, department } = prevSettings . snippetFilters || { } ;
1351- const difficultyMap = { Easy : 1 , Medium : 2 , Hard : 3 } ;
1352- const numericDifficulty = difficultyMap [ difficulty ] || null ;
1353- const category = type && type !== 'all'
1354- ? ( type === 'course_reviews' ? 'course-reviews' : type )
1355- : null ;
1356- const subject = category === 'course-reviews' && department && department !== 'all'
1357- ? department
1358- : null ;
1359- const combos = [ ] ;
1360- if ( numericDifficulty != null && category && subject ) combos . push ( { difficulty : numericDifficulty , category, subject } ) ;
1361- if ( numericDifficulty != null && category ) combos . push ( { difficulty : numericDifficulty , category } ) ;
1362- if ( numericDifficulty != null && subject ) combos . push ( { difficulty : numericDifficulty , subject } ) ;
1363- if ( numericDifficulty != null ) combos . push ( { difficulty : numericDifficulty } ) ;
1364- if ( category && subject ) combos . push ( { category, subject } ) ;
1365- if ( category ) combos . push ( { category } ) ;
1366- combos . push ( { } ) ;
1367-
1368- let found = null ;
1369- for ( const f of combos ) {
1370- const candidate = await SnippetModel . getRandom ( f ) ;
1371- if ( candidate ) {
1372- found = candidate ;
1373- break ;
1374- }
1375- }
1376- if ( ! found ) throw new Error ( 'Failed to load snippet for new match.' ) ;
1377- snippet = found ;
1378- snippetId = snippet . id ;
1379- }
1380-
1381- // Create a new lobby in the database
1382- const newLobby = await RaceModel . create ( 'private' , snippetId , hostUserId ) ;
1383- console . log ( `Created new private lobby ${ newLobby . code } (play again from ${ oldCode } )` ) ;
1384-
1385- // Build new race info in memory
1386- const newRaceInfo = {
1387- id : newLobby . id ,
1388- code : newLobby . code ,
1389- snippet : {
1390- id : snippet ?. id ,
1391- text : sanitizeSnippetText ( snippet . text ) ,
1392- is_timed_test : snippet . is_timed_test || false ,
1393- duration : snippet . duration || null ,
1394- princeton_course_url : snippet . princeton_course_url || null ,
1395- course_name : snippet . course_name || null
1396- } ,
1397- status : 'waiting' ,
1398- type : 'private' ,
1399- hostId : hostUserId ,
1400- hostNetId : hostNetid ,
1401- startTime : null ,
1402- settings : { ...prevSettings }
1403- } ;
1404- activeRaces . set ( newLobby . code , newRaceInfo ) ;
1405-
1406- // Migrate all connected players from the old lobby to the new one
1407- const newPlayers = [ ] ;
1408- const connectedOldPlayers = oldPlayers || [ ] ;
1409-
1410- for ( const player of connectedOldPlayers ) {
1411- const playerSocket = io . sockets . sockets . get ( player . id ) ;
1412- if ( ! playerSocket ) continue ; // Skip disconnected players
1413-
1414- // Leave old socket room, join new one
1415- playerSocket . leave ( oldCode ) ;
1416- playerSocket . join ( newLobby . code ) ;
1417-
1418- const isHost = player . userId === hostUserId ;
1419- const newPlayer = {
1420- id : player . id ,
1421- netid : player . netid ,
1422- userId : player . userId ,
1423- ready : isHost , // Host is implicitly ready
1424- lobbyId : newLobby . id ,
1425- snippetId : snippetId
1426- } ;
1427- newPlayers . push ( newPlayer ) ;
1428-
1429- // Add player to the new lobby in DB
1430- try {
1431- await RaceModel . addPlayerToLobby ( newLobby . id , player . userId , isHost ) ;
1432- } catch ( dbErr ) {
1433- console . error ( `Error adding player ${ player . netid } to new lobby:` , dbErr ) ;
1434- }
1435- }
1436-
1437- racePlayers . set ( newLobby . code , newPlayers ) ;
1438-
1439- // Build client data for all players
1440- const playersClientData = await Promise . all ( newPlayers . map ( p => getPlayerClientData ( p ) ) ) ;
1441-
1442- const joinedData = {
1443- code : newLobby . code ,
1444- type : 'private' ,
1445- lobbyId : newLobby . id ,
1446- hostNetId : hostNetid ,
1447- snippet : newRaceInfo . snippet ,
1448- settings : newRaceInfo . settings ,
1449- players : playersClientData
1450- } ;
1451-
1452- // Notify all players in the new room about the new lobby
1453- io . to ( newLobby . code ) . emit ( 'lobby:playAgain' , joinedData ) ;
1454-
1455- // Clean up old lobby from memory
1456- activeRaces . delete ( oldCode ) ;
1457- racePlayers . delete ( oldCode ) ;
1458-
1459- console . log ( `Play again: migrated ${ newPlayers . length } players from ${ oldCode } to ${ newLobby . code } ` ) ;
1460- if ( callback ) callback ( { success : true , lobby : joinedData } ) ;
1461-
1462- } catch ( err ) {
1463- console . error ( `Error in play again for lobby ${ oldCode } :` , err ) ;
1464- socket . emit ( 'error' , { message : err . message || 'Failed to start new match' } ) ;
1465- if ( callback ) callback ( { success : false , error : err . message || 'Failed to start new match' } ) ;
1466- }
1467- } ) ;
1468-
14691318 // --- End Private Lobby Handlers ---
14701319
14711320 // Handle player ready status
0 commit comments