From 63a7c01d4b1d4144827a8d508ff3ca9605737516 Mon Sep 17 00:00:00 2001 From: Justin Harris Date: Mon, 8 Jun 2020 23:53:59 -0400 Subject: [PATCH] client: Macros Play Relates to #8 --- .../src/components/Macros/MacroRecorder.ts | 29 ++++++ .../src/components/Macros/Macros.tsx | 91 +++++++++++++++++++ website-client/src/components/PlayGame.tsx | 10 ++ 3 files changed, 130 insertions(+) create mode 100644 website-client/src/components/Macros/MacroRecorder.ts create mode 100644 website-client/src/components/Macros/Macros.tsx diff --git a/website-client/src/components/Macros/MacroRecorder.ts b/website-client/src/components/Macros/MacroRecorder.ts new file mode 100644 index 0000000..a619a6b --- /dev/null +++ b/website-client/src/components/Macros/MacroRecorder.ts @@ -0,0 +1,29 @@ +export default class MacroRecorder { + isRecording = false + lastCommandTime = Date.now() + currentRecording: { command: string, controllerState: any }[] = [] + + start() { + this.currentRecording = [] + this.lastCommandTime = Date.now() + this.isRecording = true + } + + stop() { + this.isRecording = false + console.debug("Recorded macro:") + console.debug(this.currentRecording) + } + + add(command: string, controllerState: any) { + if (this.isRecording) { + const commandTime = Date.now() + this.currentRecording.push({ + command: `wait ${commandTime - this.lastCommandTime}`, + controllerState + }) + this.currentRecording.push({ command, controllerState }) + this.lastCommandTime = commandTime + } + } +} diff --git a/website-client/src/components/Macros/Macros.tsx b/website-client/src/components/Macros/Macros.tsx new file mode 100644 index 0000000..0405143 --- /dev/null +++ b/website-client/src/components/Macros/Macros.tsx @@ -0,0 +1,91 @@ +import { createStyles, withStyles } from '@material-ui/core'; +import Button from '@material-ui/core/Button'; +import Grid from '@material-ui/core/Grid'; +import Tooltip from '@material-ui/core/Tooltip'; +import Typography from '@material-ui/core/Typography'; +import React from 'react'; +import { SendCommand } from '../../key-binding/KeyBinding'; +import { ControllerState } from '../Controller/ControllerState'; +import MacroRecorder from './MacroRecorder'; + +const styles = () => createStyles({ +}) + +class Macros extends React.Component<{ + macroRecorder: MacroRecorder, + sendCommand: SendCommand, +}, any> { + constructor(props: any) { + super(props) + + this.state = { + isRecording: false, + macroExists: false, + } + + this.playLastRecordedMacro = this.playLastRecordedMacro.bind(this) + this.startRecording = this.startRecording.bind(this) + this.stopRecording = this.stopRecording.bind(this) + } + + startRecording(): void { + this.setState({ isRecording: true }) + this.props.macroRecorder.start() + } + + stopRecording(): void { + this.props.macroRecorder.stop() + this.setState({ isRecording: false, macroExists: true, }) + } + + async sleep(sleepMillis: number) { + return new Promise(resolve => setTimeout(resolve, sleepMillis)); + } + + async playLastRecordedMacro(): Promise { + for (const c of this.props.macroRecorder.currentRecording) { + const { command, controllerState } = c + const m = /wait (\d+)/.exec(command) + if (m) { + const sleepMillis = parseInt(m[1]) + if (sleepMillis > 100) { + const s = Date.now() + await this.sleep(sleepMillis) + } + } else { + this.props.sendCommand(command, controllerState) + } + } + } + + render(): React.ReactNode { + return
+ Macros + + + + + +
+ } +} + +export default withStyles(styles)(Macros) diff --git a/website-client/src/components/PlayGame.tsx b/website-client/src/components/PlayGame.tsx index dd6f5a5..14436a3 100644 --- a/website-client/src/components/PlayGame.tsx +++ b/website-client/src/components/PlayGame.tsx @@ -13,6 +13,8 @@ import GamepadBinding from '../key-binding/GamepadBinding' import KeyboardBinding from '../key-binding/KeyboardBinding' import Controller from './Controller/Controller' import { ControllerState } from './Controller/ControllerState' +import MacroRecorder from './Macros/MacroRecorder' +import Macros from './Macros/Macros' // Can take a Theme as input. const styles = () => createStyles({ @@ -45,6 +47,8 @@ const styles = () => createStyles({ const setupMixedContent = " You may have to enable \"mixed content\" or \"insecure content\" for this connection in your browser's settings if the server your friend is hosting does not have SSL (a link that starts with https). Warning! This is insecure." class PlayGame extends React.Component { + macroRecorder = new MacroRecorder() + constructor(props: Readonly) { super(props) @@ -215,6 +219,11 @@ class PlayGame extends React.Component { this.setState({ controllerState, }) + // TODO Find a more compact way to store controller state changes. + // Maybe they shouldn't be stored at all and we can just re-parse the command. + // That would save weird logic in other places but still keep redundancies trying to make a compact command but they rebuilding it. + // Although the rebuilding can be limited to be doing just when saving a macro. + this.macroRecorder.add(command, JSON.parse(JSON.stringify(controllerState))) } private toggleSendMode() { @@ -311,6 +320,7 @@ class PlayGame extends React.Component { mixerChannel: this.state.mixerChannel, }} /> +
URL Parameters for this page