import { useEffect, useRef, useState } from 'react'; import styles from './Emulator.module.scss'; import { GAME_SYSTEM_CORES, GameSystem, GameSystemCore } from '@/lib/game'; import { useLanguage } from '@/providers/LanguageProvider'; import { useAPI } from '@/providers/APIProvider'; type SendEmulatorMessageInit = { message:'init'; core:GameSystemCore; system:GameSystem; gameId:string; gameName:string; } type SendEmulatorMessageVolume = { message:'volume'; volume:number; }; type SendEmulatorMessage = ( SendEmulatorMessageInit | SendEmulatorMessageVolume ); type ReceiveEmulatorMessage = ( { message: 'iframe_loaded' } | { message: 'start' } | { message: 'ready' } | { message: 'save_state' } | { message: 'load_state' } | { message: 'save', data:string } | { message: 'error', error:string } ); export type EmulatorProps = { system:GameSystem; gameId:string; gameName:string; }; export const Emulator:React.FC = props => { const iframeRef = useRef(null); const [ hasInit, setHasInit ] = useState(false); const { t } = useLanguage(); const [ saving, setSaving ] = useState(false); const [ savingError, setSavingError ] = useState(null); const { saveUpdate } = useAPI(); const send = (msg:SendEmulatorMessage) => { iframeRef.current?.contentWindow?.postMessage(msg, '*'); }; const save = async (data:string) => { if(saving) return; if(!data) return; setSaving(true); try { const res = await saveUpdate({ gameId: props.gameId, data }); console.log('res', res); } catch(e) { console.error(e); } finally { setSaving(false); } } useEffect(() => { if(hasInit) return; setHasInit(true); window.onmessage = e => { if(!e.data) { console.error('Invalid message received:', e.data); return; } const msg = e.data as ReceiveEmulatorMessage; switch(msg.message) { case 'iframe_loaded': if(hasInit) return; send({ message: 'init', core: GAME_SYSTEM_CORES[props.system], system: props.system, gameId: props.gameId, gameName: props.gameName }); break; case 'save': save(msg.data).catch(console.error); break; case 'error': console.error('Emulator error:', msg.error); case 'start': case 'ready': case 'load_state': case 'save_state': break; default: console.error('Invalid message received:', msg); break } }; window.onbeforeunload = (e: BeforeUnloadEvent) => { e.preventDefault(); return t('emulator.exit_unsaved'); }; return () => { window.onbeforeunload = null; }; }, [ ]); return (