Skip to content

Commit

Permalink
client: Macros Play
Browse files Browse the repository at this point in the history
Relates to #8
  • Loading branch information
Justin Harris committed Jun 9, 2020
1 parent 923091e commit 63a7c01
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 0 deletions.
29 changes: 29 additions & 0 deletions website-client/src/components/Macros/MacroRecorder.ts
Original file line number Diff line number Diff line change
@@ -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
}
}
}
91 changes: 91 additions & 0 deletions website-client/src/components/Macros/Macros.tsx
Original file line number Diff line number Diff line change
@@ -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<void> {
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 <div>
<Typography variant="h3">Macros</Typography>
<Grid container>
<Grid item hidden={this.state.isRecording}>
<Tooltip title="Start recording a macro" placement="top">
<Button onClick={this.startRecording}>
<span role='img' aria-label="Start recording a macro">🔴</span>
</Button>
</Tooltip>
</Grid>
<Grid item hidden={!this.state.isRecording}>
<Tooltip title="Stop recording the macro" placement="top" >
<Button onClick={this.stopRecording}>
<span role='img' aria-label="Stop recording the macro"></span>
</Button>
</Tooltip>
</Grid>
<Grid item hidden={!this.state.macroExists}>
<Tooltip title="Play the last macro recorded" placement="top" >
<Button onClick={this.playLastRecordedMacro}>
<span role='img' aria-label="Play the last macro recorded"></span>
</Button>
</Tooltip>
</Grid>
</Grid>
</div>
}
}

export default withStyles(styles)(Macros)
10 changes: 10 additions & 0 deletions website-client/src/components/PlayGame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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<any, any> {
macroRecorder = new MacroRecorder()

constructor(props: Readonly<any>) {
super(props)

Expand Down Expand Up @@ -215,6 +219,11 @@ class PlayGame extends React.Component<any, any> {
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() {
Expand Down Expand Up @@ -311,6 +320,7 @@ class PlayGame extends React.Component<any, any> {
mixerChannel: this.state.mixerChannel,
}} />
</div>
<Macros macroRecorder={this.macroRecorder} sendCommand={this.sendCommand} />
<div className={classes.urlParamsInfo}>
<Typography variant="h3" >URL Parameters for this page</Typography>
<Typography component="p">
Expand Down

0 comments on commit 63a7c01

Please sign in to comment.