Full blown example.
This commit is contained in:
		
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -40,4 +40,6 @@ yarn-error.log*
 | 
				
			|||||||
*.tsbuildinfo
 | 
					*.tsbuildinfo
 | 
				
			||||||
next-env.d.ts
 | 
					next-env.d.ts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*.gbc
 | 
					*.gbc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/data
 | 
				
			||||||
@@ -66,7 +66,7 @@
 | 
				
			|||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const emulatorInit = data => {
 | 
					      const emulatorInit = data => {
 | 
				
			||||||
        EJS_gameUrl = data.game;
 | 
					        EJS_gameUrl = `/api/v1/rom?id=${data.gameId}`;
 | 
				
			||||||
        EJS_core = data.core;
 | 
					        EJS_core = data.core;
 | 
				
			||||||
        EJS_gameName = data.gameName;
 | 
					        EJS_gameName = data.gameName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -88,34 +88,35 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const { message } = data;
 | 
					        const { message } = data;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        switch(message) {
 | 
					        switch(message) {
 | 
				
			||||||
          case 'init':
 | 
					          case 'init':
 | 
				
			||||||
            emulatorInit(data);
 | 
					            emulatorInit(data);
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          case 'volume':
 | 
				
			||||||
 | 
					            EJS_emulator.setVolume(data.volume);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          default:
 | 
					          default:
 | 
				
			||||||
            console.error('Unknown message', message);
 | 
					            console.error('Unknown message', message);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      EJS_ready = (...args) => {
 | 
					      EJS_ready = () => {
 | 
				
			||||||
        send({ message: 'ready', data: args });
 | 
					        send({ message: 'ready' });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      EJS_onSaveState = (...args) => {
 | 
					      EJS_onSaveState = () => {
 | 
				
			||||||
        send({ message: 'save', data: args });
 | 
					        send({ message: 'save' });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      EJS_onLoadState = (...args) => {
 | 
					      EJS_onLoadState = () => {
 | 
				
			||||||
        send({ message: 'load', data: args });
 | 
					        send({ message: 'load' });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      EJS_onGameStart = (...args) => {
 | 
					      EJS_onGameStart = () => {
 | 
				
			||||||
        send({ message: 'start', data: args });
 | 
					        send({ message: 'start' });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    </script>
 | 
					    </script>
 | 
				
			||||||
    <!-- <script src="https://cdn.emulatorjs.org/stable/data/loader.js"></script> -->
 | 
					 | 
				
			||||||
  </body>
 | 
					  </body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
@@ -9,35 +9,40 @@ type EmulatorCore = (
 | 
				
			|||||||
  'gambatte' | 'mgba'
 | 
					  'gambatte' | 'mgba'
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const EMULATOR_SYSTEM_CORES:{ [key in EmulatorSystem]:EmulatorCore } = {
 | 
					type SendEmulatorMessageInit = {
 | 
				
			||||||
  'gb': 'gambatte',
 | 
					  message:'init';
 | 
				
			||||||
  'gbc': 'gambatte',
 | 
					  core:EmulatorCore;
 | 
				
			||||||
  'gba': 'mgba'
 | 
					  system:EmulatorSystem;
 | 
				
			||||||
 | 
					  gameId:string;
 | 
				
			||||||
 | 
					  gameName:string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SendEmulatorMessageVolume = {
 | 
				
			||||||
 | 
					  message:'volume';
 | 
				
			||||||
 | 
					  volume:number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SendEmulatorMessage = (
 | 
				
			||||||
 | 
					  SendEmulatorMessageInit |
 | 
				
			||||||
 | 
					  SendEmulatorMessageVolume
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ReceiveEmulatorMessage = (
 | 
				
			||||||
 | 
					  { message: 'start' } |
 | 
				
			||||||
 | 
					  { message: 'ready' } |
 | 
				
			||||||
 | 
					  { message: 'save' } |
 | 
				
			||||||
 | 
					  { message: 'load' }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type EmulatorProps = {
 | 
					export type EmulatorProps = {
 | 
				
			||||||
  system:EmulatorSystem;
 | 
					  system:EmulatorSystem;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SendEmulatorMessageInit = {
 | 
					const EMULATOR_SYSTEM_CORES:{ [key in EmulatorSystem]:EmulatorCore } = {
 | 
				
			||||||
  message:'init';
 | 
					  'gb': 'gambatte',
 | 
				
			||||||
  core:EmulatorCore;
 | 
					  'gbc': 'gambatte',
 | 
				
			||||||
  system:EmulatorSystem;
 | 
					  'gba': 'mgba'
 | 
				
			||||||
  game:string;
 | 
					};
 | 
				
			||||||
  gameName:string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type SendEmulatorMessage = (
 | 
					 | 
				
			||||||
  SendEmulatorMessageInit
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type ReceiveEmulatorOnGameStart = {
 | 
					 | 
				
			||||||
  message:'start'
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type ReceiveEmulatorMessage = (
 | 
					 | 
				
			||||||
  ReceiveEmulatorOnGameStart
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const Emulator:React.FC<EmulatorProps> = props => {
 | 
					export const Emulator:React.FC<EmulatorProps> = props => {
 | 
				
			||||||
  const iframeRef = useRef<HTMLIFrameElement>(null);
 | 
					  const iframeRef = useRef<HTMLIFrameElement>(null);
 | 
				
			||||||
@@ -55,7 +60,7 @@ export const Emulator:React.FC<EmulatorProps> = props => {
 | 
				
			|||||||
      message: 'init',
 | 
					      message: 'init',
 | 
				
			||||||
      core: EMULATOR_SYSTEM_CORES[props.system],
 | 
					      core: EMULATOR_SYSTEM_CORES[props.system],
 | 
				
			||||||
      system: props.system,
 | 
					      system: props.system,
 | 
				
			||||||
      game: '/test.gbc',
 | 
					      gameId: '0',
 | 
				
			||||||
      gameName: 'Pokemon - Crystal Version'
 | 
					      gameName: 'Pokemon - Crystal Version'
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -66,12 +71,12 @@ export const Emulator:React.FC<EmulatorProps> = props => {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      const msg = e.data as ReceiveEmulatorMessage;
 | 
					      const msg = e.data as ReceiveEmulatorMessage;
 | 
				
			||||||
      console.log('recv', msg);
 | 
					 | 
				
			||||||
      switch(msg.message) {
 | 
					      switch(msg.message) {
 | 
				
			||||||
        case 'start':
 | 
					        case 'start':
 | 
				
			||||||
          console.log('Game started!');
 | 
					        case 'ready':
 | 
				
			||||||
          break
 | 
					        case 'load':
 | 
				
			||||||
 | 
					        case 'save':
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
        default:
 | 
					        default:
 | 
				
			||||||
          console.error('Invalid message received:', msg);
 | 
					          console.error('Invalid message received:', msg);
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										44
									
								
								src/pages/api/v1/rom.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/pages/api/v1/rom.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					import { NextApiHandler } from "next";
 | 
				
			||||||
 | 
					import * as fs from 'fs';
 | 
				
			||||||
 | 
					import * as path from 'path';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const PATH_ROMS = path.resolve('.', 'data', 'games');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const handler:NextApiHandler = async (req, res) => {
 | 
				
			||||||
 | 
					  if (req.method !== 'GET') {
 | 
				
			||||||
 | 
					    res.setHeader('Allow', ['GET']);
 | 
				
			||||||
 | 
					    res.status(405).end(`Method Not Allowed`);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // ID query param
 | 
				
			||||||
 | 
					  if(!req.query.id || typeof req.query.id !== 'string') {
 | 
				
			||||||
 | 
					    res.status(404).end(`Not Found`);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { id } = req.query;
 | 
				
			||||||
 | 
					  if(!id) {
 | 
				
			||||||
 | 
					    res.status(404).end(`Not Found`);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Does rom exist?
 | 
				
			||||||
 | 
					  const pathRom = path.resolve(PATH_ROMS, `${id}.bin`);
 | 
				
			||||||
 | 
					  if(!fs.existsSync(pathRom)) {
 | 
				
			||||||
 | 
					    res.status(404).end(`Not Found`);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Read rom
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    const romStream = fs.createReadStream(pathRom);
 | 
				
			||||||
 | 
					    res.setHeader('Content-Type', 'application/octet-stream');
 | 
				
			||||||
 | 
					    res.setHeader('Content-Disposition', `attachment; filename="${id}.bin"`);
 | 
				
			||||||
 | 
					    romStream.pipe(res);
 | 
				
			||||||
 | 
					  } catch (error) {
 | 
				
			||||||
 | 
					    res.status(500).end(`Internal Server Error`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default handler;
 | 
				
			||||||
@@ -26,7 +26,9 @@ export const Page:React.FC<PageProps> = ({ id }) => {
 | 
				
			|||||||
      <h1>Playing Game ID: {id}</h1>
 | 
					      <h1>Playing Game ID: {id}</h1>
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      <div className={styles.play__emulator}>
 | 
					      <div className={styles.play__emulator}>
 | 
				
			||||||
        <Emulator system='gbc' />
 | 
					        <Emulator
 | 
				
			||||||
 | 
					          system='gbc'
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user