// FunConnect Client - Renderer Logic const $ = (sel) => document.querySelector(sel); const $$ = (sel) => document.querySelectorAll(sel); let isConnected = false; let currentServerUrl = ''; let appSettings = {}; // Load settings on startup (async () => { appSettings = await window.funmc.getSettings(); if (appSettings.serverUrl) $('#server-url').value = appSettings.serverUrl; if (appSettings.localPort) $('#join-local-port').value = appSettings.localPort; showRecentServers(); })(); // ===== Window Controls ===== $('#btn-min').addEventListener('click', () => window.funmc.minimize()); $('#btn-max').addEventListener('click', () => window.funmc.maximize()); $('#btn-close').addEventListener('click', () => window.funmc.close()); // ===== Navigation ===== $$('.nav-item').forEach(item => { item.addEventListener('click', () => { const page = item.dataset.page; $$('.nav-item').forEach(n => n.classList.remove('active')); item.classList.add('active'); $$('.page').forEach(p => p.classList.remove('active')); $(`#page-${page}`).classList.add('active'); }); }); // ===== Connect Page ===== $('#btn-connect').addEventListener('click', async () => { const url = $('#server-url').value.trim(); if (!url) { showMsg('#connect-msg', '请输入服务器地址', 'error'); return; } const btn = $('#btn-connect'); btn.disabled = true; btn.innerHTML = ' 连接中...'; showMsg('#connect-msg', '', ''); const result = await window.funmc.connectServer(url); if (result.success) { isConnected = true; currentServerUrl = url; updateServerStatus(true); showMsg('#connect-msg', '连接成功!', 'success'); showServerInfo(result.data); // Save to recent servers and settings await window.funmc.addRecentServer(url); await window.funmc.setSettings({ serverUrl: url }); appSettings.serverUrl = url; showRecentServers(); } else { isConnected = false; updateServerStatus(false); showMsg('#connect-msg', '连接失败: ' + result.error, 'error'); } btn.disabled = false; btn.textContent = '连接服务器'; }); function showServerInfo(data) { const grid = $('#server-info-grid'); grid.innerHTML = ''; const items = [ { label: '节点名称', value: data.nodeName || 'N/A' }, { label: '节点ID', value: data.nodeId || 'N/A' }, { label: '运行模式', value: data.isMaster ? '主节点' : '工作节点' }, { label: '运行状态', value: data.status === 'ok' ? '正常' : '异常' }, { label: '运行时间', value: formatUptime(data.uptime || 0) }, { label: '版本', value: 'v1.0.0' }, ]; items.forEach(item => { const div = document.createElement('div'); div.className = 'info-item'; div.innerHTML = `
${item.label}
${item.value}
`; grid.appendChild(div); }); $('#server-info').classList.remove('hidden'); } function updateServerStatus(online) { const dot = $('#server-status .status-dot'); const text = $('#server-status .status-text'); if (online) { dot.className = 'status-dot online'; text.textContent = '已连接'; } else { dot.className = 'status-dot offline'; text.textContent = '未连接'; } } // ===== Rooms Page ===== $('#btn-refresh-rooms').addEventListener('click', loadRooms); async function loadRooms() { if (!isConnected) { $('#rooms-list').innerHTML = '
🔗

请先连接服务器

'; return; } $('#rooms-list').innerHTML = '

加载中...

'; const result = await window.funmc.listRooms(); if (!result.success) { $('#rooms-list').innerHTML = `

${result.error}

`; return; } const rooms = result.data.rooms || []; if (rooms.length === 0) { $('#rooms-list').innerHTML = '
🎮

暂无在线房间

'; return; } $('#rooms-list').innerHTML = rooms.map(room => `
🎮
${escapeHtml(room.name)} ${room.password ? '🔒' : ''}
房间号: ${room.id} | 房主: ${escapeHtml(room.hostName)}
👥 ${room.currentPlayers}/${room.maxPlayers} ${room.gameEdition === 'java' ? 'Java' : '基岩'} ${room.gameVersion}
`).join(''); } async function quickJoin(roomId) { // Navigate to join page and fill in room ID $$('.nav-item').forEach(n => n.classList.remove('active')); document.querySelector('[data-page="join"]').classList.add('active'); $$('.page').forEach(p => p.classList.remove('active')); $('#page-join').classList.add('active'); $('#join-room-id').value = roomId; if (appSettings.localPort) $('#join-local-port').value = appSettings.localPort; } // ===== Host Page ===== $('#btn-host').addEventListener('click', async () => { if (!isConnected) { showMsg('#host-msg', '请先连接服务器', 'error'); return; } const name = $('#host-name').value.trim(); const port = parseInt($('#host-port').value); const version = $('#host-version').value.trim(); const edition = $('#host-edition').value; const maxPlayers = parseInt($('#host-maxplayers').value); const password = $('#host-password').value; if (!name) { showMsg('#host-msg', '请输入房间名称', 'error'); return; } const btn = $('#btn-host'); btn.disabled = true; btn.innerHTML = ' 创建中...'; showMsg('#host-msg', '', ''); const result = await window.funmc.hostRoom({ serverUrl: currentServerUrl, roomName: name, localMcPort: port, gameVersion: version, gameEdition: edition, maxPlayers: maxPlayers, password: password || undefined, }); if (result.success) { showMsg('#host-msg', '', ''); $('#host-room-id').textContent = result.data.room.id; $('#host-result').classList.remove('hidden'); } else { showMsg('#host-msg', '创建失败: ' + result.error, 'error'); } btn.disabled = false; btn.textContent = '创建房间'; }); $('#btn-copy-room').addEventListener('click', () => { const roomId = $('#host-room-id').textContent; navigator.clipboard.writeText(roomId).then(() => { $('#btn-copy-room').textContent = '已复制!'; setTimeout(() => { $('#btn-copy-room').textContent = '复制房间号'; }, 2000); }); }); // ===== Join Page ===== $('#btn-join').addEventListener('click', async () => { const roomId = $('#join-room-id').value.trim(); const host = $('#join-host').value.trim(); const port = parseInt($('#join-port').value); const localPort = parseInt($('#join-local-port').value); if (!roomId) { showMsg('#join-msg', '请输入房间号', 'error'); return; } if (!host) { showMsg('#join-msg', '请输入中继服务器地址', 'error'); return; } const btn = $('#btn-join'); btn.disabled = true; btn.innerHTML = ' 连接中...'; showMsg('#join-msg', '', ''); const result = await window.funmc.joinRoom({ serverHost: host, serverPort: port, roomId: roomId, localPort: localPort, }); if (result.success) { showMsg('#join-msg', '', ''); $('#join-active-port').textContent = String(localPort); $('#join-status').classList.remove('hidden'); $('#btn-join').classList.add('hidden'); $('#btn-leave').classList.remove('hidden'); } else { showMsg('#join-msg', '加入失败: ' + result.error, 'error'); } btn.disabled = false; btn.textContent = '加入房间'; }); $('#btn-leave').addEventListener('click', async () => { await window.funmc.disconnect(); $('#join-status').classList.add('hidden'); $('#btn-join').classList.remove('hidden'); $('#btn-leave').classList.add('hidden'); showMsg('#join-msg', '已断开连接', 'success'); }); // ===== Relay Status Events ===== window.funmc.onRelayStatus((data) => { if (data.status === 'disconnected') { $('#join-status').classList.add('hidden'); $('#btn-join').classList.remove('hidden'); $('#btn-leave').classList.add('hidden'); showMsg('#join-msg', '连接已断开', 'error'); } else if (data.status === 'error') { showMsg('#join-msg', '连接错误: ' + data.error, 'error'); } }); // ===== Auto-refresh rooms when navigating ===== document.querySelector('[data-page="rooms"]').addEventListener('click', () => { if (isConnected) loadRooms(); }); // ===== Settings Page ===== document.querySelector('[data-page="settings"]').addEventListener('click', loadSettings); async function loadSettings() { appSettings = await window.funmc.getSettings(); $('#settings-name').value = appSettings.playerName || ''; $('#settings-port').value = appSettings.localPort || 25566; $('#settings-autoreconnect').checked = appSettings.autoReconnect !== false; $('#settings-tray').checked = appSettings.minimizeToTray !== false; const list = $('#settings-recent-list'); const recent = appSettings.recentServers || []; if (recent.length === 0) { list.innerHTML = '
暂无记录
'; } else { list.innerHTML = recent.map(url => `
${escapeHtml(url)}
`).join(''); } } $('#btn-save-settings').addEventListener('click', async () => { const settings = { playerName: $('#settings-name').value.trim() || 'Player', localPort: parseInt($('#settings-port').value) || 25566, autoReconnect: $('#settings-autoreconnect').checked, minimizeToTray: $('#settings-tray').checked, }; await window.funmc.setSettings(settings); Object.assign(appSettings, settings); $('#join-local-port').value = settings.localPort; showMsg('#settings-msg', '设置已保存', 'success'); setTimeout(() => showMsg('#settings-msg', '', ''), 2000); }); async function showRecentServers() { const settings = await window.funmc.getSettings(); const recent = settings.recentServers || []; const container = $('#recent-servers'); if (recent.length === 0) { container.classList.add('hidden'); return; } container.classList.remove('hidden'); container.innerHTML = recent.map(url => `
${escapeHtml(url)}
`).join(''); container.querySelectorAll('.recent-item').forEach(item => { item.addEventListener('click', () => { $('#server-url').value = item.dataset.url; }); }); } // ===== Room Detail Modal ===== let currentModalRoomId = null; let modalRefreshTimer = null; function openRoomModal(roomId) { currentModalRoomId = roomId; $('#modal-overlay').classList.remove('hidden'); refreshModalRoom(); modalRefreshTimer = setInterval(refreshModalRoom, 5000); } function closeRoomModal() { currentModalRoomId = null; $('#modal-overlay').classList.add('hidden'); if (modalRefreshTimer) { clearInterval(modalRefreshTimer); modalRefreshTimer = null; } } $('#modal-close').addEventListener('click', closeRoomModal); $('#modal-overlay').addEventListener('click', (e) => { if (e.target === $('#modal-overlay')) closeRoomModal(); }); async function refreshModalRoom() { if (!currentModalRoomId || !isConnected) return; const result = await window.funmc.getRoomDetail(currentModalRoomId); if (!result || !result.success) return; const room = result.data.room; const players = result.data.players || []; $('#modal-room-name').textContent = `${room.name} ${room.password ? '🔒' : ''}`; $('#modal-room-info').innerHTML = [ { l: '房间号', v: room.id }, { l: '房主', v: room.hostName }, { l: '版本', v: `${room.gameEdition === 'java' ? 'Java' : '基岩'} ${room.gameVersion}` }, { l: '玩家', v: `${room.currentPlayers}/${room.maxPlayers}` }, ].map(i => ``).join(''); $('#modal-player-count').textContent = `(${players.length})`; if (players.length === 0) { $('#modal-player-list').innerHTML = '
暂无玩家在线
'; } else { $('#modal-player-list').innerHTML = players.map(p => `
👤 ${escapeHtml(p.name)} ${formatTimeSince(p.joinedAt)}
`).join(''); } } async function kickPlayer(roomId, playerId) { if (!confirm('确定要踢出该玩家吗?')) return; const result = await window.funmc.kickPlayer(roomId, playerId); if (result && result.success) { refreshModalRoom(); } else { alert('踢出失败: ' + (result?.error || '未知错误')); } } $('#modal-join-btn').addEventListener('click', () => { if (currentModalRoomId) { closeRoomModal(); quickJoin(currentModalRoomId); } }); $('#modal-delete-btn').addEventListener('click', async () => { if (!currentModalRoomId) return; if (!confirm('确定要删除此房间吗?此操作不可撤销。')) return; const result = await window.funmc.deleteRoom(currentModalRoomId); if (result && result.success) { closeRoomModal(); loadRooms(); } else { alert('删除失败: ' + (result?.error || '未知错误')); } }); // Chat in modal $('#modal-chat-send').addEventListener('click', sendChatMessage); $('#modal-chat-input').addEventListener('keydown', (e) => { if (e.key === 'Enter') sendChatMessage(); }); async function sendChatMessage() { const input = $('#modal-chat-input'); const msg = input.value.trim(); if (!msg || !currentModalRoomId) return; input.value = ''; const sender = appSettings.playerName || 'Player'; appendChatMessage(sender, msg); await window.funmc.sendChat(currentModalRoomId, sender, msg); } function appendChatMessage(sender, text, time) { const container = $('#modal-chat-messages'); const t = time || new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }); const div = document.createElement('div'); div.className = 'chat-msg'; div.innerHTML = `${escapeHtml(sender)} ${escapeHtml(text)}${t}`; container.appendChild(div); container.scrollTop = container.scrollHeight; } // Make room cards open modal document.addEventListener('click', (e) => { const card = e.target.closest('.room-card'); if (card && !e.target.classList.contains('room-btn-join')) { openRoomModal(card.dataset.id); } }); // ===== Utilities ===== function showMsg(selector, text, type) { const el = $(selector); el.textContent = text; el.className = 'msg' + (type ? ' ' + type : ''); } function escapeHtml(str) { const div = document.createElement('div'); div.textContent = str; return div.innerHTML; } function formatUptime(seconds) { const h = Math.floor(seconds / 3600); const m = Math.floor((seconds % 3600) / 60); const s = Math.floor(seconds % 60); if (h > 0) return `${h}时${m}分${s}秒`; if (m > 0) return `${m}分${s}秒`; return `${s}秒`; } function formatTimeSince(dateStr) { const ms = Date.now() - new Date(dateStr).getTime(); const m = Math.floor(ms / 60000); if (m < 1) return '刚刚'; if (m < 60) return `${m}分钟前`; return `${Math.floor(m / 60)}小时前`; }