mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 08:11:33 +11:00
a start of a wrapper around mgba
This commit is contained in:
parent
ef1af49a5a
commit
7098e6937b
BIN
website/public/game.gba
Executable file
BIN
website/public/game.gba
Executable file
Binary file not shown.
|
@ -1,24 +1,18 @@
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import logo from './logo.svg';
|
import { Mgba } from './mgba';
|
||||||
import './App.css';
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
|
const [onGame, setOnGame] = useState(false);
|
||||||
|
const [volume, setVolume] = useState(1.0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div>
|
||||||
<header className="App-header">
|
{
|
||||||
<img src={logo} className="App-logo" alt="logo" />
|
onGame && <><Mgba gameUrl="/game.gba" volume={volume} />
|
||||||
<p>
|
<input type="range" value={volume} min="0" max="1" step="0.05" onChange={(e) => setVolume(Number(e.target.value))}></input></>
|
||||||
Edit <code>src/App.tsx</code> and save to reload.
|
}
|
||||||
</p>
|
<button onClick={() => setOnGame(!onGame)}>{onGame ? "End Game" : "Start Game"}</button>
|
||||||
<a
|
|
||||||
className="App-link"
|
|
||||||
href="https://reactjs.org"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Learn React
|
|
||||||
</a>
|
|
||||||
</header>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import './index.css';
|
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import reportWebVitals from './reportWebVitals';
|
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(
|
const root = ReactDOM.createRoot(
|
||||||
document.getElementById('root') as HTMLElement
|
document.getElementById('root') as HTMLElement
|
||||||
|
@ -12,8 +10,3 @@ root.render(
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
|
||||||
// to log results (for example: reportWebVitals(console.log))
|
|
||||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
|
||||||
reportWebVitals();
|
|
||||||
|
|
97
website/src/mgba.tsx
Normal file
97
website/src/mgba.tsx
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
import { FC, useEffect, useRef, useState } from "react";
|
||||||
|
import mGBA from "./vendor/mgba";
|
||||||
|
|
||||||
|
type Module = any;
|
||||||
|
|
||||||
|
interface MgbaProps {
|
||||||
|
gameUrl: string,
|
||||||
|
volume?: Number,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MgbaState {
|
||||||
|
Uninitialised,
|
||||||
|
Initialising,
|
||||||
|
Initialised,
|
||||||
|
}
|
||||||
|
|
||||||
|
const MGBA_ROM_DIRECTORY = "/data/games";
|
||||||
|
|
||||||
|
|
||||||
|
export const Mgba: FC<MgbaProps> = ({ gameUrl, volume }) => {
|
||||||
|
|
||||||
|
const canvas = useRef(null);
|
||||||
|
const mgbaModule = useRef<Module>({});
|
||||||
|
|
||||||
|
const [state, setState] = useState(MgbaState.Uninitialised);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (state !== MgbaState.Initialised) return;
|
||||||
|
(async () => {
|
||||||
|
const game = await fetch(gameUrl);
|
||||||
|
const gameData = await game.arrayBuffer();
|
||||||
|
|
||||||
|
const gamePath = `${MGBA_ROM_DIRECTORY}/${gameUrl}`;
|
||||||
|
mgbaModule.current.FS.writeFile(gamePath, new Uint8Array(gameData));
|
||||||
|
mgbaModule.current.loadGame(gamePath);
|
||||||
|
})()
|
||||||
|
}, [state, gameUrl]);
|
||||||
|
|
||||||
|
// init mgba
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
|
||||||
|
if (canvas === null) return;
|
||||||
|
if (state !== MgbaState.Uninitialised) return;
|
||||||
|
|
||||||
|
setState(MgbaState.Initialising);
|
||||||
|
|
||||||
|
mgbaModule.current = {
|
||||||
|
canvas: canvas.current,
|
||||||
|
locateFile: (file: string) => {
|
||||||
|
if (file === "mgba.wasm") {
|
||||||
|
return "/vendor/mgba.wasm";
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mGBA(mgbaModule.current).then((module: Module) => {
|
||||||
|
mgbaModule.current = module;
|
||||||
|
module.FSInit();
|
||||||
|
setState(MgbaState.Initialised);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
)();
|
||||||
|
|
||||||
|
if (state === MgbaState.Initialised)
|
||||||
|
return () => {
|
||||||
|
try {
|
||||||
|
mgbaModule.current.quitGame();
|
||||||
|
mgbaModule.current.quitMgba();
|
||||||
|
} catch { }
|
||||||
|
};
|
||||||
|
}, [state]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (state !== MgbaState.Initialised) return;
|
||||||
|
mgbaModule.current.setVolume(volume ?? 1.0);
|
||||||
|
}, [state, volume]);
|
||||||
|
|
||||||
|
return <>
|
||||||
|
|
||||||
|
<canvas ref={canvas}></canvas>
|
||||||
|
<button onClick={() => {
|
||||||
|
if (state !== MgbaState.Initialised) return;
|
||||||
|
mgbaModule.current.saveState(0);
|
||||||
|
}}>Save State</button>
|
||||||
|
<button onClick={() => {
|
||||||
|
if (state !== MgbaState.Initialised) return;
|
||||||
|
mgbaModule.current.loadState(0);
|
||||||
|
}}>Load State</button>
|
||||||
|
<button onClick={() => {
|
||||||
|
if (state !== MgbaState.Initialised) return;
|
||||||
|
mgbaModule.current.quickReload(0);
|
||||||
|
}}>Restart</button>
|
||||||
|
|
||||||
|
</>;
|
||||||
|
};
|
Loading…
Reference in a new issue