mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 08:11:33 +11:00
get saving working
This commit is contained in:
parent
5365ce6188
commit
418f792ef2
|
@ -10,12 +10,11 @@ import { GbaKey, KeyBindings } from "./bindings";
|
||||||
import { styled } from "styled-components";
|
import { styled } from "styled-components";
|
||||||
import { useFrameSkip } from "./useFrameSkip.hook";
|
import { useFrameSkip } from "./useFrameSkip.hook";
|
||||||
import { useController } from "./useController.hook";
|
import { useController } from "./useController.hook";
|
||||||
|
import { useLocalStorage } from "./useLocalStorage.hook";
|
||||||
type Module = any;
|
|
||||||
|
|
||||||
interface MgbaProps {
|
interface MgbaProps {
|
||||||
gameUrl: string;
|
gameUrl: string;
|
||||||
volume?: Number;
|
volume?: number;
|
||||||
controls: KeyBindings;
|
controls: KeyBindings;
|
||||||
paused: boolean;
|
paused: boolean;
|
||||||
}
|
}
|
||||||
|
@ -55,14 +54,58 @@ async function downloadGame(gameUrl: string): Promise<ArrayBuffer> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SaveGame {
|
||||||
|
[gameName: string]: number[];
|
||||||
|
}
|
||||||
|
|
||||||
export const Mgba = forwardRef<MgbaHandle, MgbaProps>(
|
export const Mgba = forwardRef<MgbaHandle, MgbaProps>(
|
||||||
({ gameUrl, volume, controls, paused }, ref) => {
|
({ gameUrl, volume, controls, paused }, ref) => {
|
||||||
const canvas = useRef(null);
|
const canvas = useRef(null);
|
||||||
const mgbaModule = useRef<Module>({} as mGBAEmulator);
|
const mgbaModule = useRef<mGBAEmulator>();
|
||||||
|
|
||||||
|
const [saveGame, setSaveGame] = useLocalStorage<SaveGame>(
|
||||||
|
{},
|
||||||
|
"agbrswebplayer/savegames"
|
||||||
|
);
|
||||||
|
|
||||||
const [state, setState] = useState(MgbaState.Uninitialised);
|
const [state, setState] = useState(MgbaState.Uninitialised);
|
||||||
const [gameLoaded, setGameLoaded] = useState(false);
|
const [gameLoaded, setGameLoaded] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function beforeUnload() {
|
||||||
|
const gameSplit = gameUrl.split("/");
|
||||||
|
const gameBaseName = gameSplit[gameSplit.length - 1];
|
||||||
|
|
||||||
|
const save = mgbaModule.current?.getSave();
|
||||||
|
if (!save) return;
|
||||||
|
|
||||||
|
setSaveGame({
|
||||||
|
...saveGame,
|
||||||
|
[gameBaseName]: [...save],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("beforeunload", beforeUnload);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("beforeunload", beforeUnload);
|
||||||
|
};
|
||||||
|
}, [gameUrl, saveGame, setSaveGame]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (state !== MgbaState.Initialised) return;
|
||||||
|
|
||||||
|
const gameSplit = gameUrl.split("/");
|
||||||
|
const gameBaseName = gameSplit[gameSplit.length - 1];
|
||||||
|
|
||||||
|
const save = saveGame[gameBaseName];
|
||||||
|
if (!save) return;
|
||||||
|
|
||||||
|
const savePath = `${MGBA_ROM_DIRECTORY}/${gameBaseName}.sav`;
|
||||||
|
|
||||||
|
mgbaModule.current?.FS.writeFile(savePath, new Uint8Array([0, 1, 2, 3]));
|
||||||
|
}, [gameUrl, saveGame, state]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (state !== MgbaState.Initialised) return;
|
if (state !== MgbaState.Initialised) return;
|
||||||
(async () => {
|
(async () => {
|
||||||
|
@ -71,9 +114,9 @@ export const Mgba = forwardRef<MgbaHandle, MgbaProps>(
|
||||||
const gameBaseName = gameSplit[gameSplit.length - 1];
|
const gameBaseName = gameSplit[gameSplit.length - 1];
|
||||||
|
|
||||||
const gamePath = `${MGBA_ROM_DIRECTORY}/${gameBaseName}`;
|
const gamePath = `${MGBA_ROM_DIRECTORY}/${gameBaseName}`;
|
||||||
mgbaModule.current.FS.writeFile(gamePath, new Uint8Array(gameData));
|
mgbaModule.current?.FS.writeFile(gamePath, new Uint8Array(gameData));
|
||||||
mgbaModule.current.loadGame(gamePath);
|
mgbaModule.current?.loadGame(gamePath);
|
||||||
mgbaModule.current.setVolume(0.1); // for some reason you have to do this or you get no sound
|
mgbaModule.current?.setVolume(0.1); // for some reason you have to do this or you get no sound
|
||||||
setGameLoaded(true);
|
setGameLoaded(true);
|
||||||
})();
|
})();
|
||||||
}, [state, gameUrl]);
|
}, [state, gameUrl]);
|
||||||
|
@ -85,22 +128,19 @@ export const Mgba = forwardRef<MgbaHandle, MgbaProps>(
|
||||||
if (state !== MgbaState.Uninitialised) return;
|
if (state !== MgbaState.Uninitialised) return;
|
||||||
|
|
||||||
setState(MgbaState.Initialising);
|
setState(MgbaState.Initialising);
|
||||||
mgbaModule.current = {
|
|
||||||
canvas: canvas.current,
|
|
||||||
};
|
|
||||||
|
|
||||||
mGBA(mgbaModule.current).then((module: Module) => {
|
const mModule = await mGBA({ canvas: canvas.current });
|
||||||
mgbaModule.current = module;
|
mgbaModule.current = mModule;
|
||||||
module.FSInit();
|
await mModule.FSInit();
|
||||||
setState(MgbaState.Initialised);
|
await mModule.FSSync();
|
||||||
});
|
setState(MgbaState.Initialised);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
if (state === MgbaState.Initialised)
|
if (state === MgbaState.Initialised)
|
||||||
return () => {
|
return () => {
|
||||||
try {
|
try {
|
||||||
mgbaModule.current.quitGame();
|
mgbaModule.current?.quitGame();
|
||||||
mgbaModule.current.quitMgba();
|
mgbaModule.current?.quitMgba();
|
||||||
} catch {}
|
} catch {}
|
||||||
};
|
};
|
||||||
}, [state]);
|
}, [state]);
|
||||||
|
@ -119,30 +159,31 @@ export const Mgba = forwardRef<MgbaHandle, MgbaProps>(
|
||||||
? "Return"
|
? "Return"
|
||||||
: value.toLowerCase().replace("arrow", "").replace("key", "");
|
: value.toLowerCase().replace("arrow", "").replace("key", "");
|
||||||
|
|
||||||
mgbaModule.current.bindKey(binding, key);
|
mgbaModule.current?.bindKey(binding, key);
|
||||||
}
|
}
|
||||||
}, [controls, gameLoaded]);
|
}, [controls, gameLoaded]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!gameLoaded) return;
|
if (!gameLoaded) return;
|
||||||
mgbaModule.current.setVolume(volume ?? 1.0);
|
mgbaModule.current?.setVolume(volume ?? 1.0);
|
||||||
}, [gameLoaded, volume]);
|
}, [gameLoaded, volume]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!gameLoaded) return;
|
if (!gameLoaded) return;
|
||||||
|
|
||||||
if (paused) {
|
if (paused) {
|
||||||
mgbaModule.current.pauseGame();
|
mgbaModule.current?.pauseGame();
|
||||||
} else {
|
} else {
|
||||||
mgbaModule.current.resumeGame();
|
mgbaModule.current?.resumeGame();
|
||||||
}
|
}
|
||||||
}, [gameLoaded, paused]);
|
}, [gameLoaded, paused]);
|
||||||
|
|
||||||
useImperativeHandle(ref, () => {
|
useImperativeHandle(ref, () => {
|
||||||
return {
|
return {
|
||||||
restart: () => mgbaModule.current.quickReload(),
|
restart: () => mgbaModule.current?.quickReload(),
|
||||||
buttonPress: (key: GbaKey) => mgbaModule.current.buttonPress(key),
|
buttonPress: (key: GbaKey) => mgbaModule.current?.buttonPress(key),
|
||||||
buttonRelease: (key: GbaKey) => mgbaModule.current.buttonUnpress(key),
|
buttonRelease: (key: GbaKey) => mgbaModule.current?.buttonUnpress(key),
|
||||||
|
saveGame: () => {},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,9 @@ import { MutableRefObject, useEffect } from "react";
|
||||||
import { mGBAEmulator } from "./vendor/mgba";
|
import { mGBAEmulator } from "./vendor/mgba";
|
||||||
import { GbaKey } from "./bindings";
|
import { GbaKey } from "./bindings";
|
||||||
|
|
||||||
export function useController(mgbaModule: MutableRefObject<mGBAEmulator>) {
|
export function useController(
|
||||||
|
mgbaModule: MutableRefObject<mGBAEmulator | undefined>
|
||||||
|
) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let stopped = false;
|
let stopped = false;
|
||||||
|
|
||||||
|
@ -64,13 +66,13 @@ export function useController(mgbaModule: MutableRefObject<mGBAEmulator>) {
|
||||||
|
|
||||||
for (let oldButton of previouslyPressedButtons) {
|
for (let oldButton of previouslyPressedButtons) {
|
||||||
if (!currentlyPressed.has(oldButton)) {
|
if (!currentlyPressed.has(oldButton)) {
|
||||||
mgbaModule.current.buttonUnpress(oldButton);
|
mgbaModule.current?.buttonUnpress(oldButton);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let newButton of currentlyPressed) {
|
for (let newButton of currentlyPressed) {
|
||||||
if (!previouslyPressedButtons.has(newButton)) {
|
if (!previouslyPressedButtons.has(newButton)) {
|
||||||
mgbaModule.current.buttonPress(newButton);
|
mgbaModule.current?.buttonPress(newButton);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { MutableRefObject, useEffect } from "react";
|
import { MutableRefObject, useEffect } from "react";
|
||||||
import { mGBAEmulator } from "./vendor/mgba";
|
import { mGBAEmulator } from "./vendor/mgba";
|
||||||
|
|
||||||
export function useFrameSkip(mgbaModule: MutableRefObject<mGBAEmulator>) {
|
export function useFrameSkip(
|
||||||
|
mgbaModule: MutableRefObject<mGBAEmulator | undefined>
|
||||||
|
) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let previous: number | undefined = undefined;
|
let previous: number | undefined = undefined;
|
||||||
let stopped = false;
|
let stopped = false;
|
||||||
|
@ -23,12 +25,12 @@ export function useFrameSkip(mgbaModule: MutableRefObject<mGBAEmulator>) {
|
||||||
if (totalTime >= 1 / 60) {
|
if (totalTime >= 1 / 60) {
|
||||||
totalTime -= 1 / 60;
|
totalTime -= 1 / 60;
|
||||||
if (paused) {
|
if (paused) {
|
||||||
mgbaModule.current.resumeGame();
|
mgbaModule.current?.resumeGame();
|
||||||
paused = false;
|
paused = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!paused) {
|
if (!paused) {
|
||||||
mgbaModule.current.pauseGame();
|
mgbaModule.current?.pauseGame();
|
||||||
paused = true;
|
paused = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue