diff --git a/extension/background.js b/extension/background.js index 26555d4..509922b 100644 --- a/extension/background.js +++ b/extension/background.js @@ -138,6 +138,7 @@ let reconnectTimer = null; let reconnectStartTime = null; let reconnectFailed = false; let reconnectAttempts = 0; +let currentServerUrl = null; const MAX_RECONNECT_ATTEMPTS = 20; const _RECONNECT_BASE_DELAY = 500; const _RECONNECT_MAX_DELAY = 5000; @@ -272,6 +273,10 @@ function addLog(message, type = 'info') { } // --- WebSocket Client --- +function resolveServerUrl(settings) { + return (settings.serverUrl && settings.useCustomServer) ? settings.serverUrl : OFFICIAL_SERVER_URL; +} + function forceDisconnect() { if (reconnectTimer) { clearTimeout(reconnectTimer); @@ -294,6 +299,7 @@ function forceDisconnect() { socket.close(); socket = null; } + currentServerUrl = null; isConnecting = false; isNamespaceJoined = false; isForceSyncInitiator = false; @@ -377,6 +383,8 @@ async function connect() { addLog(`Connecting to ${isCustomServer ? finalUrl : 'Official Server'}... (attempt ${reconnectAttempts + 1})`, 'info'); + currentServerUrl = finalUrl; + // --- Phase 4: WebSocket Init --- try { const url = new URL(finalUrl); @@ -1230,8 +1238,20 @@ async function handleAsyncMessage(message, sender, sendResponse) { if (settings.roomId) { leaveOldRoomIfSwitching(settings.roomId); } - forceDisconnect(); - connect(); + const desiredUrl = resolveServerUrl(settings); + if (desiredUrl !== currentServerUrl || !socket || socket.readyState !== WebSocket.OPEN || !isNamespaceJoined) { + if (desiredUrl !== currentServerUrl) forceDisconnect(); + connect(); + } else if (settings.roomId) { + emit(EVENTS.JOIN_ROOM, { + roomId: settings.roomId, + password: settings.password, + peerId, + username: settings.username, + tabTitle: currentTabTitle, + protocolVersion: PROTOCOL_VERSION + }); + } sendResponse({ status: 'ok' }); } else if (message.type === 'RETRY_CONNECT') { reconnectFailed = false; @@ -1306,8 +1326,21 @@ async function handleAsyncMessage(message, sender, sendResponse) { chrome.storage.session.set({ reconnectFailed: false, reconnectAttempts: 0, reconnectStartTime: null }); broadcastConnectionStatus('connecting'); leaveOldRoomIfSwitching(roomId); - forceDisconnect(); - connect(); + const settings = await getSettings(); + const desiredUrl = resolveServerUrl(settings); + if (desiredUrl !== currentServerUrl || !socket || socket.readyState !== WebSocket.OPEN || !isNamespaceJoined) { + if (desiredUrl !== currentServerUrl) forceDisconnect(); + connect(); + } else { + emit(EVENTS.JOIN_ROOM, { + roomId, + password, + peerId, + username: settings.username, + tabTitle: currentTabTitle, + protocolVersion: PROTOCOL_VERSION + }); + } addLog(`Joining room via link: ${roomId}`, 'info'); sendResponse({ status: 'ok' }); }); diff --git a/extension/popup.js b/extension/popup.js index a32f2ac..412467d 100644 --- a/extension/popup.js +++ b/extension/popup.js @@ -1,6 +1,6 @@ import { EVENTS, OFFICIAL_LANDING_PAGE_URL } from './shared/constants.js'; import { BLACKLIST_DOMAINS } from './shared/blacklist.js'; -import { getAvatarForName, generateUsername } from './shared/names.js'; +import { getAvatarForName, generateUsername, USERNAME_ADJECTIVES, USERNAME_NOUNS } from './shared/names.js'; import { loadLocale, translateDOM, getMessage } from './i18n.js'; @@ -1018,7 +1018,7 @@ elements.joinBtn.addEventListener('click', async () => { } } - const roomId = roomIdInput || Math.random().toString(36).substring(2, 8).toUpperCase(); + const roomId = roomIdInput || generateRoomId(); let password = elements.password.value; if (isCreating && !password) { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; @@ -1048,6 +1048,13 @@ elements.leaveBtn.addEventListener('click', async () => { updateUI(null, null); }); +function generateRoomId() { + const adj = USERNAME_ADJECTIVES[Math.floor(Math.random() * USERNAME_ADJECTIVES.length)]; + const noun = USERNAME_NOUNS[Math.floor(Math.random() * USERNAME_NOUNS.length)]; + const num = Math.floor(Math.random() * 99) + 1; + return `${adj.toUpperCase()}-${noun.toUpperCase()}-${num}`; +} + function handleCreateRoom() { const secureGenerateId = (length = 6) => { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; @@ -1055,7 +1062,7 @@ function handleCreateRoom() { self.crypto.getRandomValues(array); return Array.from(array, byte => chars[byte % chars.length]).join(''); }; - const roomId = secureGenerateId(); + const roomId = generateRoomId(); const password = secureGenerateId(); elements.roomId.value = roomId; elements.password.value = password; diff --git a/shared/names.js b/shared/names.js index 2c52b45..e718f85 100644 --- a/shared/names.js +++ b/shared/names.js @@ -19,7 +19,9 @@ export const USERNAME_ADJECTIVES = [ 'Happy', 'Cool', 'Fast', 'Smart', 'Brave', 'Calm', 'Sneaky', 'Lazy', 'Wild', 'Chill', 'Lucky', 'Epic', 'Swift', 'Bold', 'Mighty', 'Cosmic', 'Neon', 'Shadow', 'Crystal', 'Thunder', 'Silent', 'Golden', - 'Fierce', 'Noble', 'Mystic', 'Frozen', 'Blazing', 'Sapphire', 'Iron', 'Crimson' + 'Fierce', 'Noble', 'Mystic', 'Frozen', 'Blazing', 'Sapphire', 'Iron', 'Crimson', + 'Turbo', 'Zen', 'Pixel', 'Cyber', 'Solar', 'Lunar', 'Astro', 'Hyper', + 'Steel', 'Rogue', 'Alpha', 'Omega', 'Royal', 'Pixel', 'Warp', 'Frost', ]; export const USERNAME_NOUNS = [ @@ -44,6 +46,8 @@ export const USERNAME_NOUNS = [ 'Beetle', 'Snail', 'Dragonfly', 'Caterpillar', 'Alien', 'Robot', 'Mermaid', 'Ghoul', 'Sprite', 'Cyborg', 'Dinosaur', 'Reaper', 'Wraith', 'Sphinx', + 'Yeti', 'Goblin', 'Pirate', 'Ninja', 'Samurai', 'Wizard', 'Knight', + 'Bandit', 'Storm', 'Comet', 'Nebula', 'Quasar', 'Vortex', ]; export const ANIMAL_EMOJI_MAP = { @@ -262,6 +266,19 @@ export const ANIMAL_EMOJI_MAP = { 'croc': '🐊', 'gnat': 'đŸĻŸ', 'gnu': 'đŸĻŦ', + 'yeti': 'đŸĻ', + 'goblin': 'đŸ‘ē', + 'pirate': '🏴', + 'ninja': 'đŸĨˇ', + 'samurai': 'đŸĨˇ', + 'wizard': '🧙', + 'knight': 'âš”ī¸', + 'bandit': 'đŸĻš', + 'storm': 'đŸŒŠī¸', + 'comet': 'â˜„ī¸', + 'nebula': '🌌', + 'quasar': '🌟', + 'vortex': 'đŸŒĒī¸', }; export function getAvatarForName(username) {