Files
homeplay-old/src/components/Emulator.tsx

134 lines
3.0 KiB
TypeScript

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 }
);
export type EmulatorProps = {
system:GameSystem;
gameId:string;
gameName:string;
};
export const Emulator:React.FC<EmulatorProps> = props => {
const iframeRef = useRef<HTMLIFrameElement>(null);
const [ hasInit, setHasInit ] = useState(false);
const { t } = useLanguage();
const [ saving, setSaving ] = useState<boolean>(false);
const [ savingError, setSavingError ] = useState<string|null>(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 '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 (
<div className={styles.emulator}>
<div className={[
styles.emulator__box,
styles[`emulator__box--${props.system}`]
].join(' ')} />
<iframe
src="/emulator.html"
className={styles.emulator__iframe}
ref={iframeRef}
/>
</div>
);
}