Adding eslint, doing eslint fixes and slowly implementing save backup from server.
This commit is contained in:
parent
67f64438b3
commit
10f04ce059
|
@ -0,0 +1,37 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true
|
||||
},
|
||||
extends: [
|
||||
'plugin:react/recommended',
|
||||
'standard-with-typescript'
|
||||
],
|
||||
overrides: [
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
project: 'tsconfig.json'
|
||||
},
|
||||
plugins: [
|
||||
'react',
|
||||
'no-relative-import-paths'
|
||||
],
|
||||
rules: {
|
||||
indent: ['error', 4, { SwitchCase: 1 }],
|
||||
'no-relative-import-paths/no-relative-import-paths': ['warn', { allowSameFolder: true }],
|
||||
'@typescript-eslint/indent': ['error', 4],
|
||||
'react/jsx-indent': ['error', 4],
|
||||
'react/jsx-indent-props': ['error', 4]
|
||||
},
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
typescript: {
|
||||
project: [
|
||||
'tsconfig.json'
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,233 +1,241 @@
|
|||
import * as React from 'react';
|
||||
import {HOME_BUTTON_IMAGE} from '/constants';
|
||||
import {sendKeyDown} from '/packet';
|
||||
import * as React from 'react'
|
||||
import { HOME_BUTTON_IMAGE } from '@msgba/constants'
|
||||
import { sendKeyDown } from '@msgba/packet'
|
||||
|
||||
export interface OverlayControlsProps {
|
||||
firstMenuElement: React.RefObject<HTMLAnchorElement>,
|
||||
setHiddenMenu: (c: boolean) => void,
|
||||
webSocket: WebSocket | null;
|
||||
firstMenuElement: React.RefObject<HTMLAnchorElement>
|
||||
setHiddenMenu: (c: boolean) => void
|
||||
webSocket: WebSocket | null
|
||||
};
|
||||
|
||||
interface ControlMap {
|
||||
[id: string]: {key: number, ref: React.RefObject<HTMLAnchorElement>, sym: string, classes: string, transformX?: number, transformY?: number}
|
||||
};
|
||||
interface ControlValue {
|
||||
key: number
|
||||
ref: React.RefObject<HTMLAnchorElement>
|
||||
sym: string
|
||||
classes: string
|
||||
transformX?: number
|
||||
transformY?: number
|
||||
}
|
||||
|
||||
export default function OverlayControls({firstMenuElement, setHiddenMenu, webSocket}: OverlayControlsProps) {
|
||||
function showOverlayMenu() {
|
||||
setHiddenMenu(false);
|
||||
type ControlMap = Record<string, ControlValue>
|
||||
|
||||
export default function OverlayControls ({ firstMenuElement, setHiddenMenu, webSocket }: OverlayControlsProps): JSX.Element {
|
||||
function showOverlayMenu (): void {
|
||||
setHiddenMenu(false)
|
||||
setTimeout(() => {
|
||||
if (firstMenuElement.current == null) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
firstMenuElement.current.focus();
|
||||
}, 100);
|
||||
firstMenuElement.current.focus()
|
||||
}, 100)
|
||||
}
|
||||
const [onGoingTouches, setOnGoingTouches] = React.useState<{[id: string]: number}>({});
|
||||
function mouseDown(e: React.MouseEvent<HTMLAnchorElement> | React.TouchEvent<HTMLAnchorElement>, key: number) {
|
||||
e.preventDefault();
|
||||
const onGoingTouches = new Map<number, number>()
|
||||
|
||||
function mouseDown (e: React.MouseEvent<HTMLAnchorElement>, key: number): void {
|
||||
e.preventDefault()
|
||||
if (webSocket == null) {
|
||||
console.log('There is not websocket');
|
||||
return;
|
||||
console.log('There is not websocket')
|
||||
return
|
||||
}
|
||||
sendKeyDown(webSocket, true, key);
|
||||
sendKeyDown(webSocket, true, key)
|
||||
}
|
||||
|
||||
function mouseUp(e: React.MouseEvent<HTMLAnchorElement> | React.TouchEvent<HTMLAnchorElement>, key: number) {
|
||||
e.preventDefault();
|
||||
function mouseUp (e: React.MouseEvent<HTMLAnchorElement>, key: number): void {
|
||||
e.preventDefault()
|
||||
if (webSocket == null) {
|
||||
console.log('There is not websocket');
|
||||
return;
|
||||
console.log('There is not websocket')
|
||||
return
|
||||
}
|
||||
sendKeyDown(webSocket, false, key);
|
||||
sendKeyDown(webSocket, false, key)
|
||||
}
|
||||
|
||||
const controls: ControlMap = {};
|
||||
const controls: ControlMap = {}
|
||||
controls.a = {
|
||||
ref: React.useRef<HTMLAnchorElement|null>(null),
|
||||
ref: React.useRef<HTMLAnchorElement | null>(null),
|
||||
key: 0,
|
||||
sym: 'A',
|
||||
classes: 'control-a control-button-a-b control control-button',
|
||||
};
|
||||
classes: 'control-a control-button-a-b control control-button'
|
||||
}
|
||||
controls.b = {
|
||||
ref: React.useRef<HTMLAnchorElement|null>(null),
|
||||
ref: React.useRef<HTMLAnchorElement | null>(null),
|
||||
key: 1,
|
||||
sym: 'B',
|
||||
transformX: 50,
|
||||
classes: 'control-b control-button-a-b control control-button',
|
||||
};
|
||||
classes: 'control-b control-button-a-b control control-button'
|
||||
}
|
||||
controls.l = {
|
||||
key: 2,
|
||||
sym: 'L',
|
||||
classes: 'control-l control-button-l-r control',
|
||||
ref: React.useRef<HTMLAnchorElement|null>(null),
|
||||
};
|
||||
ref: React.useRef<HTMLAnchorElement | null>(null)
|
||||
}
|
||||
|
||||
controls.r = {
|
||||
key: 3,
|
||||
sym: 'R',
|
||||
classes: 'control-r control-button-l-r control',
|
||||
ref: React.useRef<HTMLAnchorElement|null>(null),
|
||||
};
|
||||
ref: React.useRef<HTMLAnchorElement | null>(null)
|
||||
}
|
||||
controls.start = {
|
||||
ref: React.useRef<HTMLAnchorElement|null>(null),
|
||||
key: 4,
|
||||
sym: 'START',
|
||||
classes: 'control-start control-button-start-select control',
|
||||
transformX: 25,
|
||||
};
|
||||
ref: React.useRef<HTMLAnchorElement | null>(null)
|
||||
}
|
||||
controls.select = {
|
||||
ref: React.useRef<HTMLAnchorElement|null>(null),
|
||||
key: 5,
|
||||
sym: 'SEL',
|
||||
classes: 'control-select control-button-start-select control',
|
||||
transformX: -25,
|
||||
};
|
||||
ref: React.useRef<HTMLAnchorElement | null>(null)
|
||||
}
|
||||
controls.up = {
|
||||
ref: React.useRef<HTMLAnchorElement|null>(null),
|
||||
key: 6,
|
||||
sym: '^',
|
||||
transformX: 100,
|
||||
classes: 'control-up control control-pad-button',
|
||||
ref: React.useRef<HTMLAnchorElement | null>(null)
|
||||
}
|
||||
controls.down = {
|
||||
ref: React.useRef<HTMLAnchorElement|null>(null),
|
||||
key: 7,
|
||||
sym: 'v',
|
||||
transformX: 100,
|
||||
classes: 'control-down control control-pad-button',
|
||||
ref: React.useRef<HTMLAnchorElement | null>(null)
|
||||
}
|
||||
controls.left = {
|
||||
ref: React.useRef<HTMLAnchorElement|null>(null),
|
||||
key: 8,
|
||||
sym: '<',
|
||||
classes: 'control-left control control-pad-button',
|
||||
ref: React.useRef<HTMLAnchorElement | null>(null)
|
||||
}
|
||||
controls.right = {
|
||||
ref: React.useRef<HTMLAnchorElement|null>(null),
|
||||
key: 9,
|
||||
sym: '>',
|
||||
transformX: 200,
|
||||
classes: 'control-right control control-pad-button',
|
||||
ref: React.useRef<HTMLAnchorElement | null>(null)
|
||||
}
|
||||
|
||||
function determineKey(e: React.TouchEvent<HTMLDivElement>, touch: React.Touch): number | null {
|
||||
const x = touch.pageX;
|
||||
const y = touch.pageY;
|
||||
function determineKey (e: React.TouchEvent<HTMLDivElement>, touch: React.Touch): number | null {
|
||||
const x = touch.pageX
|
||||
const y = touch.pageY
|
||||
for (const control of Object.keys(controls)) {
|
||||
const ref = controls[control].ref.current;
|
||||
const ref = controls[control].ref.current
|
||||
if (ref == null) {
|
||||
console.log('No ref found');
|
||||
continue;
|
||||
console.log('No ref found')
|
||||
continue
|
||||
}
|
||||
let top = ref.getBoundingClientRect().top + document.documentElement.scrollTop;
|
||||
const currentControl = controls[control];
|
||||
const transformX = currentControl.transformX;
|
||||
const transformY = currentControl.transformY;
|
||||
let offsetLeft = ref.offsetLeft;
|
||||
const offsetWidth = ref.offsetWidth;
|
||||
let offsetTop = top;
|
||||
const offsetHeight = ref.offsetHeight;
|
||||
const top = ref.getBoundingClientRect().top + document.documentElement.scrollTop
|
||||
const currentControl = controls[control]
|
||||
const transformX = currentControl.transformX
|
||||
const transformY = currentControl.transformY
|
||||
let offsetLeft = ref.offsetLeft
|
||||
const offsetWidth = ref.offsetWidth
|
||||
let offsetTop = top
|
||||
const offsetHeight = ref.offsetHeight
|
||||
if (transformX != null) {
|
||||
offsetLeft += offsetWidth * (transformX / 100);
|
||||
offsetLeft += offsetWidth * (transformX / 100)
|
||||
}
|
||||
if (transformY != null) {
|
||||
offsetTop += offsetHeight * (transformY / 100);
|
||||
offsetTop += offsetHeight * (transformY / 100)
|
||||
}
|
||||
console.log(x, y, offsetLeft, offsetTop, offsetWidth, offsetHeight);
|
||||
if (x >= offsetLeft && x <= offsetLeft + offsetWidth && y >= offsetTop && y <= offsetTop + offsetHeight) {
|
||||
return controls[control].key;
|
||||
return controls[control].key
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
function touchStartControls(e: React.TouchEvent<HTMLDivElement>) {
|
||||
e.preventDefault();
|
||||
function touchStartControls (e: React.TouchEvent<HTMLDivElement>): boolean {
|
||||
e.preventDefault()
|
||||
if (webSocket == null) {
|
||||
console.log('There is not websocket');
|
||||
return;
|
||||
console.log('There is not websocket')
|
||||
return false
|
||||
}
|
||||
for (let i = 0; i < e.changedTouches.length; i++) {
|
||||
const touch = e.changedTouches[i];
|
||||
let key: number|null = determineKey(e, touch);
|
||||
const touch = e.changedTouches[i]
|
||||
const key: number | null = determineKey(e, touch)
|
||||
if (key == null) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
const idx = touch.identifier;
|
||||
onGoingTouches[idx] = key;
|
||||
sendKeyDown(webSocket, true, key);
|
||||
const idx = touch.identifier
|
||||
onGoingTouches.set(idx, key)
|
||||
sendKeyDown(webSocket, true, key)
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
function touchMoveControls(e: React.TouchEvent<HTMLDivElement>) {
|
||||
e.preventDefault();
|
||||
function touchMoveControls (e: React.TouchEvent<HTMLDivElement>): boolean {
|
||||
e.preventDefault()
|
||||
if (webSocket == null) {
|
||||
console.log('There is not websocket');
|
||||
return;
|
||||
console.log('There is not websocket')
|
||||
return false
|
||||
}
|
||||
for (let i = 0; i < e.changedTouches.length; i++) {
|
||||
const touch = e.changedTouches[i];
|
||||
let key: number|null = determineKey(e, touch);
|
||||
const idx = touch.identifier;
|
||||
const touch = e.changedTouches[i]
|
||||
const key: number | null = determineKey(e, touch)
|
||||
const idx = touch.identifier
|
||||
if (key == null) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
if (onGoingTouches[idx] != null) {
|
||||
sendKeyDown(webSocket, false, onGoingTouches[idx]);
|
||||
delete onGoingTouches[idx];
|
||||
const oldKey = onGoingTouches.get(idx)
|
||||
if (oldKey != null) {
|
||||
sendKeyDown(webSocket, false, oldKey)
|
||||
onGoingTouches.delete(idx)
|
||||
}
|
||||
onGoingTouches[idx] = key;
|
||||
sendKeyDown(webSocket, true, key);
|
||||
onGoingTouches.set(idx, key)
|
||||
sendKeyDown(webSocket, true, key)
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
function touchEndControls(e: React.TouchEvent<HTMLDivElement>) {
|
||||
e.preventDefault();
|
||||
function touchEndControls (e: React.TouchEvent<HTMLDivElement>): boolean {
|
||||
e.preventDefault()
|
||||
if (webSocket == null) {
|
||||
console.log('There is not websocket');
|
||||
return;
|
||||
console.log('There is not websocket')
|
||||
return false
|
||||
}
|
||||
for (let i = 0; i < e.changedTouches.length; i++) {
|
||||
const touch = e.changedTouches[i];
|
||||
const idx = touch.identifier;
|
||||
if (onGoingTouches[idx] == null) {
|
||||
return;
|
||||
const touch = e.changedTouches[i]
|
||||
const idx = touch.identifier
|
||||
const oldKey = onGoingTouches.get(idx)
|
||||
if (oldKey == null) {
|
||||
return false
|
||||
}
|
||||
sendKeyDown(webSocket, false, onGoingTouches[idx]);
|
||||
delete onGoingTouches[idx];
|
||||
sendKeyDown(webSocket, false, oldKey)
|
||||
onGoingTouches.delete(idx)
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
const keyMap: string[] = ["KeyZ", "KeyX", "KeyA", "KeyS",
|
||||
"Enter", "Space", "ArrowUp", "ArrowDown",
|
||||
"ArrowLeft", "ArrowRight"];
|
||||
function onPressControl(e: React.KeyboardEvent<HTMLDivElement>) {
|
||||
const keyMap: string[] = ['KeyZ', 'KeyX', 'KeyA', 'KeyS',
|
||||
'Enter', 'Space', 'ArrowUp', 'ArrowDown',
|
||||
'ArrowLeft', 'ArrowRight']
|
||||
function onPressControl (e: React.KeyboardEvent<HTMLDivElement>): void {
|
||||
if (webSocket == null) {
|
||||
console.log('There is not websocket');
|
||||
return;
|
||||
console.log('There is not websocket')
|
||||
return
|
||||
}
|
||||
let key = keyMap.findIndex((c: string) => c == e.code);
|
||||
if (key != -1) {
|
||||
e.preventDefault();
|
||||
sendKeyDown(webSocket, true, key);
|
||||
const key = keyMap.findIndex((c: string) => c === e.code)
|
||||
if (key !== -1) {
|
||||
e.preventDefault()
|
||||
sendKeyDown(webSocket, true, key)
|
||||
}
|
||||
}
|
||||
function onUnpressControl(e: React.KeyboardEvent<HTMLDivElement>) {
|
||||
function onUnpressControl (e: React.KeyboardEvent<HTMLDivElement>): void {
|
||||
if (webSocket == null) {
|
||||
console.log('There is not websocket');
|
||||
return;
|
||||
console.log('There is not websocket')
|
||||
return
|
||||
}
|
||||
let key = keyMap.findIndex((c: string) => c == e.code);
|
||||
if (key != -1) {
|
||||
e.preventDefault();
|
||||
sendKeyDown(webSocket, false, key);
|
||||
const key = keyMap.findIndex((c: string) => c === e.code)
|
||||
if (key !== -1) {
|
||||
e.preventDefault()
|
||||
sendKeyDown(webSocket, false, key)
|
||||
}
|
||||
}
|
||||
document.onselectstart = () => false;
|
||||
document.onselectstart = () => false
|
||||
return (
|
||||
<div tabIndex={-1} className="overlay" onKeyDown={onPressControl} onKeyUp={onUnpressControl}>
|
||||
<div tabIndex={-1} className="overlay" onKeyDown={onPressControl} onKeyUp={onUnpressControl}>
|
||||
<div className="vertical-padding">
|
||||
</div>
|
||||
<div className="controls" onTouchStart={touchStartControls} onTouchMove={touchMoveControls} onTouchEnd={touchEndControls}>
|
||||
|
@ -235,18 +243,18 @@ export default function OverlayControls({firstMenuElement, setHiddenMenu, webSoc
|
|||
<img src={HOME_BUTTON_IMAGE} alt="Go to menu. (House icon)"/>
|
||||
</a>
|
||||
{
|
||||
Object.keys(controls).map((key) =>
|
||||
Object.keys(controls).map((key) =>
|
||||
<a tabIndex={-1}
|
||||
className={controls[key].classes}
|
||||
ref={controls[key].ref}
|
||||
key={key}
|
||||
onMouseDown={(e: React.MouseEvent<HTMLAnchorElement>) => mouseDown(e, controls[key].key)}
|
||||
onMouseUp={(e: React.MouseEvent<HTMLAnchorElement>) => mouseUp(e, controls[key].key)}>
|
||||
className={controls[key].classes}
|
||||
ref={controls[key].ref}
|
||||
key={key}
|
||||
onMouseDown={(e: React.MouseEvent<HTMLAnchorElement>) => { mouseDown(e, controls[key].key) }}
|
||||
onMouseUp={(e: React.MouseEvent<HTMLAnchorElement>) => { mouseUp(e, controls[key].key) }}>
|
||||
{controls[key].sym}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,59 +1,79 @@
|
|||
import * as React from 'react';
|
||||
import CloseButton from '/components/close-button';
|
||||
import {CLOSE_BUTTON_IMAGE} from '/constants';
|
||||
import * as React from 'react'
|
||||
import CloseButton from '@msgba/components/close-button'
|
||||
import { sendSaveRequest } from '@msgba/packet'
|
||||
|
||||
export interface OverlayMenuProps {
|
||||
hiddenMenu: boolean,
|
||||
setHiddenMenu: (c: boolean) => void,
|
||||
emulationStarted: boolean,
|
||||
setHiddenFormSelectFiles: (c: boolean) => void,
|
||||
screenRef: React.RefObject<HTMLDivElement>;
|
||||
isFullscreen: boolean;
|
||||
setIsFullscreen: (c:boolean) => void;
|
||||
firstMenuElement: React.RefObject<HTMLAnchorElement>,
|
||||
hiddenMenu: boolean
|
||||
setHiddenMenu: (c: boolean) => void
|
||||
emulationStarted: boolean
|
||||
setHiddenFormSelectFiles: (c: boolean) => void
|
||||
screenRef: React.RefObject<HTMLDivElement>
|
||||
isFullscreen: boolean
|
||||
setIsFullscreen: (c: boolean) => void
|
||||
firstMenuElement: React.RefObject<HTMLAnchorElement>
|
||||
websocket: WebSocket | null
|
||||
};
|
||||
|
||||
interface Style {
|
||||
[id: string]: string;
|
||||
};
|
||||
type Style = Record<string, string>
|
||||
|
||||
export default function OverlayMenu({hiddenMenu, setHiddenMenu, emulationStarted,
|
||||
setHiddenFormSelectFiles, screenRef, isFullscreen, setIsFullscreen,
|
||||
firstMenuElement}: OverlayMenuProps) {
|
||||
function exitMenu() {
|
||||
setHiddenMenu(true);
|
||||
export default function OverlayMenu ({
|
||||
hiddenMenu, setHiddenMenu, emulationStarted,
|
||||
setHiddenFormSelectFiles, screenRef, isFullscreen, setIsFullscreen,
|
||||
firstMenuElement, websocket
|
||||
}: OverlayMenuProps): JSX.Element {
|
||||
function exitMenu (): void {
|
||||
setHiddenMenu(true)
|
||||
}
|
||||
|
||||
function openSelectFilesMenu() {
|
||||
setHiddenFormSelectFiles(false);
|
||||
function openSelectFilesMenu (): void {
|
||||
setHiddenFormSelectFiles(false)
|
||||
}
|
||||
|
||||
function toggleFullscreen() {
|
||||
function toggleFullscreen (): void {
|
||||
if (isFullscreen) {
|
||||
document.exitFullscreen();
|
||||
setIsFullscreen(false);
|
||||
return;
|
||||
document.exitFullscreen().catch((c: string) => { console.log(c) })
|
||||
setIsFullscreen(false)
|
||||
}
|
||||
|
||||
if (screenRef.current != null) {
|
||||
screenRef.current.requestFullscreen().then(() => {
|
||||
setIsFullscreen(true);
|
||||
});
|
||||
setIsFullscreen(true)
|
||||
}).catch((error: string) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const styleSelectRom: Style = {};
|
||||
const styleSelectRom: Style = {}
|
||||
if (emulationStarted) {
|
||||
styleSelectRom.display = 'none';
|
||||
styleSelectRom.display = 'none'
|
||||
}
|
||||
|
||||
const styleMenu: Style = {};
|
||||
const styleMenu: Style = {}
|
||||
if (hiddenMenu) {
|
||||
styleMenu.display = 'none';
|
||||
styleMenu.display = 'none'
|
||||
}
|
||||
let toggleFullscreenText: string = 'Set fullscreen';
|
||||
let toggleFullscreenText: string = 'Set fullscreen'
|
||||
if (isFullscreen) {
|
||||
toggleFullscreenText = 'End fullscreen';
|
||||
toggleFullscreenText = 'End fullscreen'
|
||||
}
|
||||
|
||||
const saveButton = React.useRef<HTMLAnchorElement>(null)
|
||||
const [saveIdentifier, setSaveidentifier] = React.useState<bigint>(0n)
|
||||
|
||||
function onSave (): void {
|
||||
if (websocket == null) {
|
||||
console.log('No websocket still')
|
||||
return
|
||||
}
|
||||
const currentSave = saveIdentifier
|
||||
setSaveidentifier((c: bigint) => c + 1n)
|
||||
sendSaveRequest(websocket, currentSave)
|
||||
}
|
||||
|
||||
const styleSave: Style = {}
|
||||
if (!emulationStarted) {
|
||||
styleSave.display = 'none'
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -64,10 +84,11 @@ export default function OverlayMenu({hiddenMenu, setHiddenMenu, emulationStarted
|
|||
<div className="overlay-menu">
|
||||
<ul>
|
||||
<li><a ref={firstMenuElement} style={styleSelectRom} onClick={openSelectFilesMenu} href="#">Select rom</a></li>
|
||||
<li><a ref={saveButton} style={styleSave} onClick={onSave} href="#">Save</a></li>
|
||||
<li><a onClick={toggleFullscreen} href="#">{toggleFullscreenText}</a></li>
|
||||
<li><a href="#" onClick={exitMenu}>Exit</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,141 +1,147 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import CenterElement from '/components/center-element';
|
||||
import FormSelectFiles from '/components/form-select-files';
|
||||
import CanvasGBAEmulator from '/components/canvas-gba-emulator';
|
||||
import OverlayControls from '/components/overlay-controls';
|
||||
import OverlayMenu from '/components/overlay-menu';
|
||||
import OverlaySelectFiles from '/components/overlay-select-files';
|
||||
import CloseButton from '/components/close-button';
|
||||
import {sendHello, handleSendFrame} from '/packet';
|
||||
import CenterElement from '@msgba/components/center-element'
|
||||
import CanvasGBAEmulator from '@msgba/components/canvas-gba-emulator'
|
||||
import OverlayControls from '@msgba/components/overlay-controls'
|
||||
import OverlayMenu from '@msgba/components/overlay-menu'
|
||||
import OverlaySelectFiles from '@msgba/components/overlay-select-files'
|
||||
import { sendHello, handleSendFrame } from '@msgba/packet'
|
||||
|
||||
import Endian from '/endian';
|
||||
import Endian from '@msgba/endian'
|
||||
|
||||
import {MIN_WIDTH, MIN_HEIGHT, PACKET_ID_HELLO, PACKET_ID_SEND_FRAME} from '/constants';
|
||||
import { MIN_WIDTH, MIN_HEIGHT, PACKET_ID_SEND_FRAME } from '@msgba/constants'
|
||||
|
||||
export interface handleClickStartEmulationButtonObjectArgs {
|
||||
e: React.MouseEvent<HTMLInputElement>;
|
||||
inputRom: HTMLInputElement | null;
|
||||
inputSaveState: HTMLInputElement | null;
|
||||
canvas: React.RefObject<HTMLCanvasElement | null>;
|
||||
setEmulationStarted: (c: boolean) => void,
|
||||
setHiddenMenu: (c: boolean) => void;
|
||||
setHiddenFormSelectFiles: (c: boolean) => void;
|
||||
setWebSocket: (c: WebSocket | null) => void;
|
||||
};
|
||||
e: React.MouseEvent<HTMLInputElement>
|
||||
inputRom: HTMLInputElement | null
|
||||
inputSaveState: HTMLInputElement | null
|
||||
canvas: React.RefObject<HTMLCanvasElement | null>
|
||||
setEmulationStarted: (c: boolean) => void
|
||||
setHiddenMenu: (c: boolean) => void
|
||||
setHiddenFormSelectFiles: (c: boolean) => void
|
||||
setWebSocket: (c: WebSocket | null) => void
|
||||
}
|
||||
|
||||
function handleClickStartEmulationButton({e, inputRom, inputSaveState, canvas, setEmulationStarted, setHiddenMenu,
|
||||
setHiddenFormSelectFiles, setWebSocket}: handleClickStartEmulationButtonObjectArgs) {
|
||||
e.preventDefault();
|
||||
function handleClickStartEmulationButton ({
|
||||
e, inputRom, inputSaveState, canvas,
|
||||
setEmulationStarted, setHiddenMenu,
|
||||
setHiddenFormSelectFiles, setWebSocket
|
||||
}: handleClickStartEmulationButtonObjectArgs): void {
|
||||
e.preventDefault()
|
||||
if (canvas.current == null) {
|
||||
alert('Canvas does not exists?');
|
||||
return;
|
||||
alert('Canvas does not exists?')
|
||||
return
|
||||
}
|
||||
const ctx = canvas.current.getContext('2d')
|
||||
if (ctx == null) {
|
||||
alert('Unable to create canvas context, doing nothing');
|
||||
return;
|
||||
alert('Unable to create canvas context, doing nothing')
|
||||
return
|
||||
}
|
||||
if (inputRom == null || inputSaveState == null || inputRom.files == null || inputSaveState.files == null) {
|
||||
alert('Unable to read the files ');
|
||||
return;
|
||||
alert('Unable to read the files ')
|
||||
return
|
||||
}
|
||||
if (inputRom.files.length == 0) {
|
||||
alert('There is no rom still');
|
||||
return;
|
||||
if (inputRom.files.length === 0) {
|
||||
alert('There is no rom still')
|
||||
return
|
||||
}
|
||||
if (inputSaveState.files.length == 0) {
|
||||
alert('There is no savestate still');
|
||||
return;
|
||||
if (inputSaveState.files.length === 0) {
|
||||
alert('There is no savestate still')
|
||||
return
|
||||
}
|
||||
const rom_file = inputRom.files[0];
|
||||
const savestate_file = inputSaveState.files[0];
|
||||
rom_file.arrayBuffer().then((rom_buffer) => {
|
||||
savestate_file.arrayBuffer().then((savestate_buffer) => {
|
||||
const rom_array = new Uint8Array(rom_buffer);
|
||||
const savestate_array = new Uint8Array(savestate_buffer);
|
||||
const webSocket = new WebSocket(`ws://${window.location.host}/ws`);
|
||||
setWebSocket(webSocket);
|
||||
webSocket.binaryType = 'arraybuffer';
|
||||
const romFile = inputRom.files[0]
|
||||
const savestateFile = inputSaveState.files[0]
|
||||
romFile.arrayBuffer().then((romBuffer) => {
|
||||
savestateFile.arrayBuffer().then((savestateBuffer) => {
|
||||
const romArray = new Uint8Array(romBuffer)
|
||||
const savestateArray = new Uint8Array(savestateBuffer)
|
||||
const webSocket = new WebSocket(`ws://${window.location.host}/ws`)
|
||||
setWebSocket(webSocket)
|
||||
webSocket.binaryType = 'arraybuffer'
|
||||
webSocket.onclose = (message) => {
|
||||
setEmulationStarted(false);
|
||||
console.log('Closing websocket.');
|
||||
setWebSocket(null);
|
||||
setEmulationStarted(false)
|
||||
console.log('Closing websocket.')
|
||||
setWebSocket(null)
|
||||
}
|
||||
webSocket.onopen = () => {
|
||||
console.log('Opened websocket.');
|
||||
setEmulationStarted(true);
|
||||
sendHello(webSocket, rom_array, savestate_array);
|
||||
setHiddenMenu(true);
|
||||
setHiddenFormSelectFiles(true);
|
||||
};
|
||||
console.log('Opened websocket.')
|
||||
setEmulationStarted(true)
|
||||
sendHello(webSocket, romArray, savestateArray)
|
||||
setHiddenMenu(true)
|
||||
setHiddenFormSelectFiles(true)
|
||||
}
|
||||
webSocket.addEventListener('message', (event) => {
|
||||
onWebSocketPacket(event, canvas.current, ctx)
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
}).catch((c: string) => {
|
||||
console.log('Unable to convert file to array_buffer')
|
||||
})
|
||||
}).catch((c: string) => {
|
||||
console.log('Unable to convert file to array_buffer')
|
||||
})
|
||||
}
|
||||
|
||||
function onWebSocketPacket(event: MessageEvent, canvas: HTMLCanvasElement | null, ctx: CanvasRenderingContext2D) {
|
||||
const buffer = event.data;
|
||||
let packet_u8: Uint8Array | null = new Uint8Array(buffer);
|
||||
const id = Endian.byteArrayToU64BigEndian(packet_u8.slice(0, 8));
|
||||
packet_u8 = packet_u8.slice(8, packet_u8.length);
|
||||
const size = Endian.byteArrayToU64BigEndian(packet_u8.slice(0, 8));
|
||||
const raw_data = packet_u8.slice(8, packet_u8.length);
|
||||
packet_u8 = null;
|
||||
function onWebSocketPacket (event: MessageEvent,
|
||||
canvas: HTMLCanvasElement | null,
|
||||
ctx: CanvasRenderingContext2D): void {
|
||||
const buffer = event.data
|
||||
let packetU8: Uint8Array | null = new Uint8Array(buffer)
|
||||
const id = Endian.byteArrayToU64BigEndian(packetU8.slice(0, 8))
|
||||
packetU8 = packetU8.slice(8, packetU8.length)
|
||||
const size = Endian.byteArrayToU64BigEndian(packetU8.slice(0, 8))
|
||||
const rawData = packetU8.slice(8, Number(size))
|
||||
packetU8 = null
|
||||
switch (id) {
|
||||
case PACKET_ID_SEND_FRAME:
|
||||
handleSendFrame(raw_data, canvas, ctx);
|
||||
break;
|
||||
handleSendFrame(rawData, canvas, ctx)
|
||||
break
|
||||
default:
|
||||
console.log(`Received unknown packet ${id}`);
|
||||
console.log(`Received unknown packet ${id}`)
|
||||
}
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
const screenDimensions = useScreenDimensions();
|
||||
const emulatorDimensions = calculateSizeEmulator(screenDimensions);
|
||||
const canvasRef = React.useRef<HTMLCanvasElement>(null);
|
||||
function resizeCanvas() {
|
||||
const canvas = canvasRef.current;
|
||||
export default function Page (): JSX.Element {
|
||||
const screenDimensions = useScreenDimensions()
|
||||
const emulatorDimensions = calculateSizeEmulator(screenDimensions)
|
||||
const canvasRef = React.useRef<HTMLCanvasElement>(null)
|
||||
function resizeCanvas (): void {
|
||||
const canvas = canvasRef.current
|
||||
if (canvas == null) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (emulatorDimensions.width === undefined || emulatorDimensions.height === undefined) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
canvas.width = emulatorDimensions.width;
|
||||
canvas.height = emulatorDimensions.height;
|
||||
canvas.width = emulatorDimensions.width
|
||||
canvas.height = emulatorDimensions.height
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (ctx == null) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
fillBlack(canvas, ctx);
|
||||
};
|
||||
const [hiddenFormSelectFiles, setHiddenFormSelectFiles] = React.useState<boolean>(true);
|
||||
React.useEffect(resizeCanvas, [emulatorDimensions]);
|
||||
const refInputRom = React.useRef<HTMLInputElement | null>(null);
|
||||
const refInputSaveState = React.useRef<HTMLInputElement | null>(null);
|
||||
const [emulationStarted, setEmulationStarted] = React.useState<boolean>(false);
|
||||
const [hiddenMenu, setHiddenMenu] = React.useState<boolean>(true);
|
||||
const [webSocket, setWebSocket] = React.useState<WebSocket | null>(null);
|
||||
const onStartEmulation = (e: React.MouseEvent<HTMLInputElement>) => {
|
||||
fillBlack(canvas, ctx)
|
||||
}
|
||||
const [hiddenFormSelectFiles, setHiddenFormSelectFiles] = React.useState<boolean>(true)
|
||||
React.useEffect(resizeCanvas, [emulatorDimensions])
|
||||
const refInputRom = React.useRef<HTMLInputElement | null>(null)
|
||||
const refInputSaveState = React.useRef<HTMLInputElement | null>(null)
|
||||
const [emulationStarted, setEmulationStarted] = React.useState<boolean>(false)
|
||||
const [hiddenMenu, setHiddenMenu] = React.useState<boolean>(true)
|
||||
const [webSocket, setWebSocket] = React.useState<WebSocket | null>(null)
|
||||
const onStartEmulation = (e: React.MouseEvent<HTMLInputElement>): void => {
|
||||
handleClickStartEmulationButton({
|
||||
e: e,
|
||||
setEmulationStarted: setEmulationStarted,
|
||||
e,
|
||||
setEmulationStarted,
|
||||
inputRom: refInputRom.current,
|
||||
inputSaveState: refInputSaveState.current,
|
||||
canvas: canvasRef,
|
||||
setHiddenMenu: setHiddenMenu,
|
||||
setHiddenFormSelectFiles: setHiddenFormSelectFiles,
|
||||
setWebSocket: setWebSocket,
|
||||
|
||||
});
|
||||
};
|
||||
const firstMenuElement = React.useRef<HTMLAnchorElement>(null);
|
||||
const screenRef = React.useRef<HTMLDivElement>(null);
|
||||
const [isFullscreen, setIsFullscreen] = React.useState<boolean>(false);
|
||||
setHiddenMenu,
|
||||
setHiddenFormSelectFiles,
|
||||
setWebSocket
|
||||
})
|
||||
}
|
||||
const firstMenuElement = React.useRef<HTMLAnchorElement>(null)
|
||||
const screenRef = React.useRef<HTMLDivElement>(null)
|
||||
const [isFullscreen, setIsFullscreen] = React.useState<boolean>(false)
|
||||
return (
|
||||
<div ref={screenRef}>
|
||||
<OverlayControls firstMenuElement={firstMenuElement}
|
||||
|
@ -144,78 +150,78 @@ export default function Page() {
|
|||
setHiddenMenu={setHiddenMenu} emulationStarted={emulationStarted}
|
||||
setHiddenFormSelectFiles={setHiddenFormSelectFiles} screenRef={screenRef}
|
||||
isFullscreen={isFullscreen} setIsFullscreen={setIsFullscreen}
|
||||
firstMenuElement={firstMenuElement}/>
|
||||
firstMenuElement={firstMenuElement} websocket={webSocket}/>
|
||||
<OverlaySelectFiles hiddenFormSelectFiles={hiddenFormSelectFiles}
|
||||
setHiddenFormSelectFiles={setHiddenFormSelectFiles}
|
||||
refInputRom={refInputRom} refInputSaveState={refInputSaveState}
|
||||
onStartEmulation={onStartEmulation}/>
|
||||
<div>
|
||||
<CenterElement full={true}>
|
||||
<CanvasGBAEmulator canvasRef={canvasRef}/>
|
||||
<CanvasGBAEmulator canvasRef={canvasRef}/>
|
||||
</CenterElement>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function getScreenDimensions() {
|
||||
function getScreenDimensions (): EmulatorDimensions {
|
||||
return {
|
||||
width: document.body.clientWidth,
|
||||
height: document.body.clientHeight
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function useScreenDimensions() {
|
||||
const [screenDimensions, setScreenDimensions] = React.useState<EmulatorDimensions>(getScreenDimensions());
|
||||
function useScreenDimensions (): EmulatorDimensions {
|
||||
const [screenDimensions, setScreenDimensions] = React.useState<EmulatorDimensions>(getScreenDimensions())
|
||||
|
||||
React.useEffect(() => {
|
||||
function onResize() {
|
||||
setScreenDimensions(getScreenDimensions());
|
||||
function onResize (): void {
|
||||
setScreenDimensions(getScreenDimensions())
|
||||
}
|
||||
|
||||
window.addEventListener("resize", onResize);
|
||||
|
||||
window.addEventListener('resize', onResize)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", onResize);
|
||||
window.removeEventListener('resize', onResize)
|
||||
}
|
||||
}, []);
|
||||
|
||||
return screenDimensions;
|
||||
}, [])
|
||||
return screenDimensions
|
||||
}
|
||||
|
||||
function fillBlack(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) {
|
||||
ctx.beginPath();
|
||||
ctx.rect(0, 0, canvas.width, canvas.height);
|
||||
ctx.fillStyle = '#0E0E10';
|
||||
ctx.fill();
|
||||
function fillBlack (canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D): void {
|
||||
ctx.beginPath()
|
||||
ctx.rect(0, 0, canvas.width, canvas.height)
|
||||
ctx.fillStyle = '#0E0E10'
|
||||
ctx.fill()
|
||||
}
|
||||
|
||||
export interface EmulatorDimensions {
|
||||
width?: number;
|
||||
height?: number;
|
||||
width?: number
|
||||
height?: number
|
||||
};
|
||||
function calculateSizeEmulator(screenDimensions: EmulatorDimensions): EmulatorDimensions {
|
||||
|
||||
function calculateSizeEmulator (screenDimensions: EmulatorDimensions): EmulatorDimensions {
|
||||
if (screenDimensions.width === undefined || screenDimensions.height === undefined) {
|
||||
console.error(screenDimensions, 'screenDimensions has undefined fields');
|
||||
return {};
|
||||
console.error(screenDimensions, 'screenDimensions has undefined fields')
|
||||
return {}
|
||||
}
|
||||
const width = screenDimensions.width;
|
||||
const height = screenDimensions.height;
|
||||
const emulatorDimensions: EmulatorDimensions = {};
|
||||
const width = screenDimensions.width
|
||||
const height = screenDimensions.height
|
||||
const emulatorDimensions: EmulatorDimensions = {}
|
||||
if (width < MIN_WIDTH || height < MIN_HEIGHT) {
|
||||
return {
|
||||
width: MIN_WIDTH,
|
||||
height: MIN_HEIGHT,
|
||||
};
|
||||
height: MIN_HEIGHT
|
||||
}
|
||||
}
|
||||
const ratioWidth = width / MIN_WIDTH;
|
||||
const ratioHeight = height / MIN_HEIGHT;
|
||||
const ratioWidth = width / MIN_WIDTH
|
||||
const ratioHeight = height / MIN_HEIGHT
|
||||
if (ratioWidth < ratioHeight) {
|
||||
emulatorDimensions.width = MIN_WIDTH * ratioWidth;
|
||||
emulatorDimensions.height = MIN_HEIGHT * ratioWidth;
|
||||
emulatorDimensions.width = MIN_WIDTH * ratioWidth
|
||||
emulatorDimensions.height = MIN_HEIGHT * ratioWidth
|
||||
} else {
|
||||
emulatorDimensions.height = MIN_HEIGHT * ratioHeight;
|
||||
emulatorDimensions.width = MIN_WIDTH * ratioHeight;
|
||||
emulatorDimensions.height = MIN_HEIGHT * ratioHeight
|
||||
emulatorDimensions.width = MIN_WIDTH * ratioHeight
|
||||
}
|
||||
return emulatorDimensions;
|
||||
return emulatorDimensions
|
||||
}
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
export const MIN_WIDTH = 240;
|
||||
export const MIN_HEIGHT = 160;
|
||||
export const PACKET_ID_HELLO = 0n;
|
||||
export const PACKET_ID_SEND_FRAME = 1n;
|
||||
export const PACKET_ID_KEY_DOWN = 2n;
|
||||
export const CLOSE_BUTTON_IMAGE: string = "/img/close.png";
|
||||
export const HOME_BUTTON_IMAGE: string = "/img/home.png";
|
||||
|
||||
export default class Constants {
|
||||
public static MIN_WIDTH: number = MIN_WIDTH;
|
||||
public static MIN_HEIGHT: number = MIN_HEIGHT;
|
||||
public static PACKET_ID_HELLO: bigint = PACKET_ID_HELLO;
|
||||
public static PACKET_ID_SEND_FRAME: bigint = PACKET_ID_SEND_FRAME;
|
||||
public static CLOSE_BUTTON_IMAGE: string = CLOSE_BUTTON_IMAGE;
|
||||
public static HOME_BUTTON_IMAGE: string = HOME_BUTTON_IMAGE;
|
||||
};
|
||||
export const MIN_WIDTH = 240
|
||||
export const MIN_HEIGHT = 160
|
||||
export const PACKET_ID_HELLO = 0n
|
||||
export const PACKET_ID_SEND_FRAME = 1n
|
||||
export const PACKET_ID_KEY_DOWN = 2n
|
||||
export const PACKET_ID_SAVE_REQUEST = 3n
|
||||
export const PACKET_ID_SAVE_RESPONSE = 4n
|
||||
export const CLOSE_BUTTON_IMAGE: string = '/img/close.png'
|
||||
export const HOME_BUTTON_IMAGE: string = '/img/home.png'
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDOMClient from 'react-dom/client';
|
||||
|
||||
import Endian from '/endian';
|
||||
import Page from '/components/page';
|
||||
import Endian from '@msgba/endian';
|
||||
import Page from '@msgba/components/page';
|
||||
|
||||
const body = document.querySelector('body');
|
||||
if (body != null) {
|
||||
|
|
110
js-src/packet.ts
110
js-src/packet.ts
|
@ -1,70 +1,78 @@
|
|||
import Endian from '/endian';
|
||||
import {MIN_WIDTH, MIN_HEIGHT, PACKET_ID_HELLO, PACKET_ID_SEND_FRAME, PACKET_ID_KEY_DOWN} from '/constants';
|
||||
import Endian from '@msgba/endian'
|
||||
import { MIN_WIDTH, MIN_HEIGHT, PACKET_ID_HELLO, PACKET_ID_KEY_DOWN, PACKET_ID_SAVE_REQUEST } from '@msgba/constants'
|
||||
|
||||
function concatU8Array(array1: Uint8Array, array2: Uint8Array) {
|
||||
const final_array = new Uint8Array(array1.length + array2.length);
|
||||
final_array.set(array1);
|
||||
final_array.set(array2, array1.length);
|
||||
return final_array;
|
||||
function concatU8Array (array1: Uint8Array, array2: Uint8Array): Uint8Array {
|
||||
const finalArray = new Uint8Array(array1.length + array2.length)
|
||||
finalArray.set(array1)
|
||||
finalArray.set(array2, array1.length)
|
||||
return finalArray
|
||||
}
|
||||
|
||||
export function sendHello(websocket: WebSocket, rom_array: Uint8Array, savestate_array: Uint8Array) {
|
||||
console.log('Sending hello.');
|
||||
const length_rom = BigInt(rom_array.length);
|
||||
const length_savestate = BigInt(savestate_array.length);
|
||||
const raw_data =
|
||||
export function sendHello (websocket: WebSocket, romArray: Uint8Array, savestateArray: Uint8Array): void {
|
||||
console.log('Sending hello.')
|
||||
const lengthRom = BigInt(romArray.length)
|
||||
const lengthSavestate = BigInt(savestateArray.length)
|
||||
const rawData =
|
||||
concatU8Array(
|
||||
concatU8Array(
|
||||
concatU8Array(Endian.u64ToByteArrayBigEndian(length_rom), rom_array),
|
||||
Endian.u64ToByteArrayBigEndian(length_savestate)
|
||||
concatU8Array(Endian.u64ToByteArrayBigEndian(lengthRom), romArray),
|
||||
Endian.u64ToByteArrayBigEndian(lengthSavestate)
|
||||
),
|
||||
savestate_array
|
||||
);
|
||||
sendPacket(websocket, PACKET_ID_HELLO, raw_data);
|
||||
savestateArray
|
||||
)
|
||||
sendPacket(websocket, PACKET_ID_HELLO, rawData)
|
||||
}
|
||||
|
||||
export function sendKeyDown(websocket: WebSocket, isDown: boolean, key: number) {
|
||||
console.log('Sending keyDown.', isDown);
|
||||
const isDownArray = new Uint8Array(1);
|
||||
isDownArray[0] = isDown ? 1: 0;
|
||||
const rawData = concatU8Array(isDownArray, Endian.u32ToByteArrayBigEndian(key));
|
||||
sendPacket(websocket, PACKET_ID_KEY_DOWN, rawData);
|
||||
export function sendKeyDown (websocket: WebSocket, isDown: boolean, key: number): void {
|
||||
console.log('Sending keyDown.', isDown)
|
||||
const isDownArray = new Uint8Array(1)
|
||||
isDownArray[0] = isDown ? 1 : 0
|
||||
const rawData = concatU8Array(isDownArray, Endian.u32ToByteArrayBigEndian(key))
|
||||
sendPacket(websocket, PACKET_ID_KEY_DOWN, rawData)
|
||||
}
|
||||
|
||||
export function sendPacket(websocket: WebSocket, id: bigint, raw_data: Uint8Array) {
|
||||
const packet_u8 = concatU8Array(
|
||||
concatU8Array(Endian.u64ToByteArrayBigEndian(id), Endian.u64ToByteArrayBigEndian(BigInt(raw_data.length))),
|
||||
raw_data
|
||||
);
|
||||
const packet_buffer = packet_u8.buffer;
|
||||
console.log('Sending packet');
|
||||
websocket.send(packet_buffer);
|
||||
export function sendSaveRequest (websocket: WebSocket, identifier: bigint): void {
|
||||
console.log('Sendidng save request', identifier)
|
||||
const rawData = Endian.u64ToByteArrayBigEndian(identifier)
|
||||
sendPacket(websocket, PACKET_ID_SAVE_REQUEST, rawData)
|
||||
}
|
||||
|
||||
export function handleSendFrame(raw_data: Uint8Array, canvas: HTMLCanvasElement | null, ctx: CanvasRenderingContext2D) {
|
||||
console.log('Reachs here');
|
||||
export function sendPacket (websocket: WebSocket, id: bigint, rawData: Uint8Array): void {
|
||||
const packetU8 = concatU8Array(
|
||||
concatU8Array(Endian.u64ToByteArrayBigEndian(id), Endian.u64ToByteArrayBigEndian(BigInt(rawData.length))),
|
||||
rawData
|
||||
)
|
||||
const packetBuffer = packetU8.buffer
|
||||
console.log('Sending packet')
|
||||
websocket.send(packetBuffer)
|
||||
}
|
||||
|
||||
export function handleSendFrame (rawData: Uint8Array, canvas: HTMLCanvasElement | null, ctx: CanvasRenderingContext2D): void {
|
||||
console.log('Reachs here')
|
||||
if (canvas == null) {
|
||||
console.log('No canvas');
|
||||
return;
|
||||
console.log('No canvas')
|
||||
return
|
||||
}
|
||||
let data: Uint8Array | null = raw_data;
|
||||
const stride = Endian.byteArrayToU32BigEndian(data.slice(0, 4));
|
||||
data = data.slice(4, data.length);
|
||||
const output_buffer_size = Endian.byteArrayToU64BigEndian(data.slice(0, 8));
|
||||
data = data.slice(8, data.length);
|
||||
const img_data = ctx.createImageData(MIN_WIDTH, MIN_HEIGHT);
|
||||
const img_data_u8 = new Uint8Array(img_data.data.buffer);
|
||||
for (let i = 0; i<data.length; i++) {
|
||||
if (i % 4 == 3) {
|
||||
img_data_u8[i] = 255;
|
||||
continue;
|
||||
let data: Uint8Array | null = rawData
|
||||
data = data.slice(4, data.length)
|
||||
const outputBufferSize = Endian.byteArrayToU64BigEndian(data.slice(0, 8))
|
||||
// TODO: This number conversion is not great. Is there other option?
|
||||
data = data.slice(8, Number(outputBufferSize))
|
||||
const imgData = ctx.createImageData(MIN_WIDTH, MIN_HEIGHT)
|
||||
const imgDataU8 = new Uint8Array(imgData.data.buffer)
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (i % 4 === 3) {
|
||||
imgDataU8[i] = 255
|
||||
continue
|
||||
}
|
||||
img_data_u8[i] = data[i];
|
||||
imgDataU8[i] = data[i]
|
||||
}
|
||||
data = null;
|
||||
createImageBitmap(img_data).then((bitmap) => drawBitmap(bitmap, canvas, ctx));
|
||||
data = null
|
||||
createImageBitmap(imgData).then((bitmap) => { drawBitmap(bitmap, canvas, ctx) }).catch((c: string) => {
|
||||
console.log(`Unable to print to the canvas the frame because: ${c}`)
|
||||
})
|
||||
}
|
||||
|
||||
function drawBitmap(bitmap: ImageBitmap, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) {
|
||||
ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
|
||||
function drawBitmap (bitmap: ImageBitmap, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D): void {
|
||||
ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height)
|
||||
}
|
||||
|
|
10
package.json
10
package.json
|
@ -13,10 +13,20 @@
|
|||
"@babel/preset-react": "^7.18.6",
|
||||
"@types/react": "^18.0.28",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.0",
|
||||
"babel-loader": "^9.1.2",
|
||||
"eslint": "^8.36.0",
|
||||
"eslint-config-standard-with-typescript": "^34.0.1",
|
||||
"eslint-import-resolver-typescript": "^3.5.3",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-n": "^15.6.1",
|
||||
"eslint-plugin-no-relative-import-paths": "^1.5.2",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "^5.0.2",
|
||||
"typescript-transform-paths": "^3.4.6",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.38.1",
|
||||
"webpack-cli": "^4.7.2"
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -10,8 +10,21 @@
|
|||
"strictNullChecks": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"*": ["js-src/*"]
|
||||
}
|
||||
"@msgba/*": ["js-src/*"],
|
||||
"/*": ["js-src/*"]
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"transform": "typescript-transform-paths"
|
||||
},
|
||||
{
|
||||
"transform": "typescript-transform-paths",
|
||||
"afterDeclarations": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": ["js-src/*.ts", "js-src/*/*.ts" ]
|
||||
"include": [
|
||||
"js-src/*.ts", "js-src/*/*.ts",
|
||||
"js-src/*.tsx", "js-src/*/*.tsx"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,35 +1,38 @@
|
|||
const path = require('path');
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
entry: './js-src/index.tsx',
|
||||
mode: 'development',
|
||||
output: {
|
||||
filename: 'bundle.js',
|
||||
path: path.resolve(__dirname, 'public/js/'),
|
||||
path: path.resolve(__dirname, 'public/js/')
|
||||
},
|
||||
resolve: {
|
||||
extensions: [ '.js', '.jsx','.ts', '.tsx' ],
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||
roots: [
|
||||
path.resolve(__dirname, 'js-src/')
|
||||
]
|
||||
],
|
||||
alias: {
|
||||
'@msgba': path.resolve(__dirname, 'js-src')
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.jpe?g|png$/,
|
||||
exclude: /node_modules/,
|
||||
use: ["url-loader", "file-loader"]
|
||||
use: ['url-loader', 'file-loader']
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
exclude: /node_modules/,
|
||||
loader: "babel-loader"
|
||||
loader: 'babel-loader'
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue