diff --git a/www/html/Admin/private/store.json b/www/html/Admin/private/store.json index 48d9fa8..bb956b5 100644 --- a/www/html/Admin/private/store.json +++ b/www/html/Admin/private/store.json @@ -733,6 +733,18 @@ "achievements": { "d1_minigame_solver": 2 } + }, + { + "id": "4647f699cdb5676b884008f8", + "email": "", + "displayName": "Guest", + "loginType": "guest", + "providerUserId": "p_1782187303882_g81ebi60min", + "notes": "auto: player-coins", + "blocked": false, + "coins": 0, + "createdAt": "2026-06-23T04:01:44+00:00", + "updatedAt": "2026-06-23T04:01:44+00:00" } ] } \ No newline at end of file diff --git a/www/html/Create Room/create-room.js b/www/html/Create Room/create-room.js index 1814a7a..30d7927 100644 --- a/www/html/Create Room/create-room.js +++ b/www/html/Create Room/create-room.js @@ -199,6 +199,7 @@ name: name, isPrivate: isPrivate, maxPlayers: maxPlayersForSlot(selectedSlot), + password: isPrivate ? getPrivatePinDigits() : '', }; fetch(SERVER + '/api/spaces', { method: 'POST', @@ -229,6 +230,7 @@ } catch (e) {} var lobbyUrl = BASE + '/room-lobby.html?space=' + encodeURIComponent(sid) + '&nick=' + encodeURIComponent(getNick()); lobbyUrl += '&displayRoom=' + encodeURIComponent(name); + if (isPrivate) lobbyUrl += '&pass=' + encodeURIComponent(getPrivatePinDigits()); /* host เข้าห้องตัวเองผ่านด่านรหัส */ try { var caseId = localStorage.getItem('caseId'); var prevSpaceName = localStorage.getItem('prevSpaceName'); diff --git a/www/html/Create Room/index.html b/www/html/Create Room/index.html index 57dae5d..f88cb25 100644 --- a/www/html/Create Room/index.html +++ b/www/html/Create Room/index.html @@ -87,7 +87,7 @@ - + - +
v —
diff --git a/www/html/Game/server.js b/www/html/Game/server.js index a94f83b..8d0ff5c 100644 --- a/www/html/Game/server.js +++ b/www/html/Game/server.js @@ -5922,15 +5922,15 @@ const server = http.createServer((req, res) => { if (!mapData) return res.writeHead(400), res.end(JSON.stringify({ ok: false, error: 'ไม่พบฉาก' })); const isPrivate = !!d.isPrivate; let sid; + let roomPassword = ''; if (isPrivate) { - const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; - let code; - for (let i = 0; i < 20; i++) { - code = ''; - for (let j = 0; j < 6; j++) code += chars[Math.floor(Math.random() * chars.length)]; - if (!spaces.has(code)) { sid = code; break; } - } - if (!sid) sid = 'priv-' + Date.now(); + /* ห้องส่วนตัว: ใช้ "ชื่อห้อง" เป็น spaceId (canonical = trim+lowercase) → join ด้วยชื่อ+รหัสได้ + (เดิมเป็นรหัสสุ่ม 6 ตัว ทำให้ join ด้วยชื่อไม่เจอ) */ + const canon = String(d.name || '').trim().toLowerCase(); + if (!canon) return res.writeHead(400), res.end(JSON.stringify({ ok: false, error: 'กรุณาใส่ชื่อห้อง' })); + if (spaces.has(canon)) return res.writeHead(409), res.end(JSON.stringify({ ok: false, error: 'ชื่อห้องนี้ถูกใช้แล้ว ลองชื่ออื่น' })); + sid = canon; + roomPassword = String(d.password == null ? '' : d.password).replace(/\D/g, '').slice(0, 4); } else { const base = (d.name || '').trim() || 'space'; sid = base + '-' + Date.now(); @@ -5949,6 +5949,7 @@ const server = http.createServer((req, res) => { botSlotCount = effectiveBotSlotCount({ botSlotCount, maxPlayers }); spaces.set(sid, { mapData, mapId, peers: new Map(), spaceName, hostId: null, isPrivate, maxPlayers, createdAt: Date.now(), + roomPassword, previewEditorTest, botSlotCount, suspectCardMinigames: null, @@ -7475,9 +7476,22 @@ io.on('connection', (socket) => { if (qbAllAnswered(room)) qbReveal(room); }); - socket.on('join-space', ({ spaceId, nickname, characterId, playMapId, playerKey, desiredLobbyColorThemeIndex, desiredLobbySkinToneIndex, bannedSpectator }, cb) => { + socket.on('join-space', ({ spaceId, nickname, characterId, playMapId, playerKey, desiredLobbyColorThemeIndex, desiredLobbySkinToneIndex, bannedSpectator, roomPassword }, cb) => { const space = spaces.get(spaceId); if (!space || !space.mapData) return cb && cb({ ok: false, error: 'ไม่พบห้อง' }); + /* ห้องส่วนตัวที่ตั้งรหัส → ต้องกรอกรหัสให้ตรง ครั้งแรก แล้วจำด้วย playerKey + (กันด่านรหัสพังตอนเปลี่ยนหน้า LobbyA→play→LobbyB ที่ socket.id เปลี่ยน — ไม่ต้องแนบ pass ทุกลิงก์) */ + if (space.isPrivate && space.roomPassword) { + space.privateVerifiedKeys = space.privateVerifiedKeys || new Set(); + const pk = String(playerKey == null ? '' : playerKey); + const provided = String(roomPassword == null ? '' : roomPassword).replace(/\D/g, '').slice(0, 4); + const passOk = provided === space.roomPassword; + const alreadyOk = (pk && space.privateVerifiedKeys.has(pk)) || space.peers.has(socket.id); + if (!passOk && !alreadyOk) { + return cb && cb({ ok: false, error: 'รหัสผ่านห้องไม่ถูกต้อง' }); + } + if (passOk && pk) space.privateVerifiedKeys.add(pk); + } const maxPlayers = space.maxPlayers || 10; const mdJoinPre = (space.mapId && maps.get(space.mapId)) || space.mapData; if (mdJoinPre && mdJoinPre.gameType === 'gauntlet' && space.peers.size >= GAUNTLET_MAX_PLAYERS) { diff --git a/www/html/Join Room/index.html b/www/html/Join Room/index.html index 9387f2a..b6b2694 100644 --- a/www/html/Join Room/index.html +++ b/www/html/Join Room/index.html @@ -82,6 +82,6 @@ - + diff --git a/www/html/Join Room/join.js b/www/html/Join Room/join.js index dfce9f7..b48f98a 100644 --- a/www/html/Join Room/join.js +++ b/www/html/Join Room/join.js @@ -191,11 +191,13 @@ }); btnPrivateJoin?.addEventListener('click', function () { - /* รหัสห้องเป็นตัวพิมพ์ใหญ่เสมอ (ABCDEFGHJKLMNPQRSTUVWXYZ23456789) — บังคับ uppercase กันพิมพ์เล็กแล้วหาห้องไม่เจอ (404) */ - var code = (idPrivate?.value || '').trim().toUpperCase(); + /* ห้องส่วนตัว join ด้วย "ชื่อห้อง" + รหัสผ่าน — spaceId ฝั่ง server = ชื่อแบบ canonical (trim+lowercase) + (เดิม uppercase เป็นรหัสสุ่ม → พิมพ์ชื่อห้องแล้วหาไม่เจอ) */ + var raw = (idPrivate?.value || '').trim(); + var code = raw.toLowerCase(); var pass = (passPrivate?.value || '').trim(); if (!code) { - alert('กรุณากรอกรหัสห้อง'); + alert('กรุณากรอกชื่อห้อง'); idPrivate?.focus(); return; } @@ -204,7 +206,11 @@ passPrivate?.focus(); return; } - goToRoom(code); + var url = BASE + '/room-lobby.html?space=' + encodeURIComponent(code) + + '&nick=' + encodeURIComponent(getNick()) + + '&displayRoom=' + encodeURIComponent(raw); + if (pass) url += '&pass=' + encodeURIComponent(pass); + window.location.href = url; }); if (USE_PUBLIC_ROOM_MOCK) {