import React, { useState, useEffect, useRef } from 'react'; import { Link } from 'react-router-dom'; import { Users, Clock, Circle, ServerOff } from 'lucide-react'; import { motion } from 'framer-motion'; import { io } from 'socket.io-client'; import { formatPlayTimeMs } from '../utils/formatters'; // 스티브 머리 기본 이미지 (로딩 전/실패 시 사용) const STEVE_HEAD_BASE64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABWUlEQVRoge2ZPUsDQRCGk7DeGU1ysTDYpIuFjYUasMpHaZFKhJDWxj7gjwjYK/Y2wcJKsAppxEYkTcCU1oGcubt8gv6Bd4oBk3FhnvLZvb17GW7YvYsfF3Z+YgxSjuFMZxPMl9BPCZ9Y5cOsAw0gjQaQxvjBBA54qST0VJfgdqe/Wsf6CmgAaTSANMZ1NuDAVfkU+r3cLl5oKw39MhpDP5ktoPf9EfR33Tfora+ABpBGA0gTv788gycyz8vCC5Iu7loUVLfhQnUn6yugAaTRANIYqttc3DxAf15qQV8vfkBPdY/nzwr0j91r6NvNBvTWV0ADSKMBpGHvhSg6733W/MrRAWu+7oX+KxpAGusDmDCK4ECwCKF/ee1Bf5jfZ934tv0Efa16Av13iE921ldAA0ijAaQxxsU/6odj/NbPpnPoe18D1nx304Ge6jaZbfw9yvoKaABpNIA0v2NsVwyhlV0PAAAAAElFTkSuQmCC'; // 플레이어 아바타 컴포넌트 - 스킨 캐싱 API 사용 const PlayerAvatar = ({ uuid, name }) => { const [src, setSrc] = useState(STEVE_HEAD_BASE64); useEffect(() => { // 스킨 캐싱 API 호출 (avatar/uuid/size) fetch(`/link/skin/avatar/${uuid}/48`) .then(res => res.json()) .then(data => { if (data.url) { const img = new Image(); img.onload = () => setSrc(data.url); img.onerror = () => setSrc(`https://mc-heads.net/avatar/${uuid}/48`); img.src = data.url; } }) .catch(() => { // 폴백: mc-heads 직접 사용 setSrc(`https://mc-heads.net/avatar/${uuid}/48`); }); }, [uuid]); return ( {name} ); }; // 전체 플레이어 목록 페이지 const PlayersPage = ({ isMobile = false }) => { const [players, setPlayers] = useState([]); const [loading, setLoading] = useState(true); const [serverOnline, setServerOnline] = useState(null); const socketRef = useRef(null); useEffect(() => { // 소켓 연결 const socket = io('/', { path: '/socket.io', transports: ['websocket', 'polling'] }); socketRef.current = socket; socket.on('status', (data) => { setServerOnline(data.online); if (!data.online) { setLoading(false); } }); socket.on('players', (data) => { setPlayers(data); setLoading(false); }); // 1초마다 갱신 const interval = setInterval(() => { socket.emit('get_players'); }, 1000); // 초기 요청 socket.emit('get_players'); return () => { clearInterval(interval); socket.disconnect(); }; }, []); // 온라인 플레이어 먼저, 그 다음 이름순 정렬 const sortedPlayers = [...players].sort((a, b) => { if (a.isOnline !== b.isOnline) return b.isOnline ? 1 : -1; return a.name.localeCompare(b.name, 'ko'); }); const onlineCount = players.filter(p => p.isOnline).length; // 서버 오프라인 상태 - 전체 화면 가운데 정렬 if (serverOnline === false) { return (

서버 오프라인

마인크래프트 서버가 현재 오프라인 상태입니다.
서버가 시작되면 플레이어 정보를 확인할 수 있습니다.

); } return (
{/* 헤더 */}

플레이어

전체 플레이어 {players.length} 온라인 {onlineCount}명

{/* 플레이어 목록 */} {loading ? (
) : sortedPlayers.length > 0 ? (
{sortedPlayers.map((player, index) => ( {/* 플레이어 아바타 */}
{/* 온라인 상태 표시 */}
{/* 플레이어 정보 */}

{player.name}

{player.isOnline ? '온라인' : '오프라인'}
{formatPlayTimeMs(player.totalPlayTimeMs)}
{/* 통계 보기 안내 */}
통계 보기 →
))}
) : (

등록된 플레이어가 없습니다.

)}
); }; export default PlayersPage;