Skip to content

Commit

Permalink
feat: more experiments with game window stream
Browse files Browse the repository at this point in the history
  • Loading branch information
KatoakDR committed Jan 1, 2024
1 parent bcdcd10 commit 501ae6c
Showing 1 changed file with 126 additions and 4 deletions.
130 changes: 126 additions & 4 deletions electron/renderer/pages/home.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { EuiListGroup, EuiListGroupItem, EuiPanel } from '@elastic/eui';
import { type ReactNode, useCallback, useState } from 'react';
import { runInBackground } from '../../common/async';
import { useObservable, useSubscription } from 'observable-hooks';
import type { ReactNode } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import * as rxjs from 'rxjs';
import { runInBackground, sleep } from '../../common/async';
import { equalsIgnoreCase } from '../../common/string';
import { useLogger } from '../components/logger';

Expand All @@ -10,13 +13,47 @@ interface IpcSgeCharacter {
characterName: string;
}

// I started tracking this via `useState` but when calling it's setter
// the value did not update fast enough before a text game event
// was received, resulting in text routing to the wrong stream window.
let gameStreamId = '';

const HomePage: React.FC = (): ReactNode => {
const { logger } = useLogger('page:home');

// TODO for each runInBackground, need to catch and display errors

// TODO add state to track when any of the callbacks are running
// so that we show a loading indicator or overlay or something
// to prevent the user issuing more commands concurrently

const gameEventsSubject$ = useObservable(() => {
return new rxjs.Subject<{ type: string } & Record<string, any>>();
});

useSubscription(gameEventsSubject$, (gameEvent) => {
switch (gameEvent.type) {
case 'TEXT':
appendGameText(`[${gameStreamId}] ${gameEvent.text}`);
break;
case 'POP_STREAM':
gameStreamId = '';
break;
case 'PUSH_STREAM':
gameStreamId = gameEvent.streamId;
break;
}
});

const [gameText, setGameText] = useState<Array<string>>([]);

const appendGameText = useCallback((newText: string) => {
// TODO get user's scrollback buffer preference
const scrollbackBuffer = 500; // max number of most recent lines to keep
newText = newText.replace(/\n/g, '<br/>');
setGameText((texts) => texts.concat(newText).slice(scrollbackBuffer * -1));
}, []);

const [characters, setCharacters] = useState<Array<IpcSgeCharacter>>([]);

const [playingCharacter, setPlayingCharacter] = useState<
Expand All @@ -27,8 +64,6 @@ const HomePage: React.FC = (): ReactNode => {
setCharacters(await window.api.listCharacters());
}, []);

// TODO for each runInBackground, need to catch and display errors

const quitCharacter = useCallback(async () => {
if (playingCharacter) {
const characterName = playingCharacter.characterName;
Expand Down Expand Up @@ -161,6 +196,25 @@ const HomePage: React.FC = (): ReactNode => {
gameCode,
});
//--
await sleep(2000);
await window.api.sendCommand('health');
await sleep(1000);
await window.api.sendCommand('info');
await sleep(1000);
await window.api.sendCommand('experience');
await sleep(1000);
await window.api.sendCommand('out');
await sleep(1000);
await window.api.sendCommand('out');
await sleep(1000);
await window.api.sendCommand('perceive');
await sleep(10_000);
await window.api.sendCommand('spell');
await sleep(1000);
await window.api.sendCommand('go bank');
await sleep(1000);
await window.api.sendCommand('go window');
//--
},
[logger]
);
Expand All @@ -178,6 +232,66 @@ const HomePage: React.FC = (): ReactNode => {
[playCharacter]
);

useEffect(() => {
window.api.onMessage(
'game:connect',
(_event, { accountName, characterName, gameCode }) => {
logger.info('game:connect', { accountName, characterName, gameCode });
}
);

return () => {
window.api.removeAllListeners('game:connect');
};
}, [logger]);

useEffect(() => {
window.api.onMessage(
'game:disconnect',
(_event, { accountName, characterName, gameCode }) => {
logger.info('game:disconnect', {
accountName,
characterName,
gameCode,
});
}
);

return () => {
window.api.removeAllListeners('game:disconnect');
};
}, [logger]);

useEffect(() => {
window.api.onMessage('game:error', (_event, error: Error) => {
logger.error('game:error', { error });
});

return () => {
window.api.removeAllListeners('game:error');
};
}, [logger]);

useEffect(() => {
window.api.onMessage('game:event', (_event, gameEvent) => {
logger.info('game:event', { gameEvent });
gameEventsSubject$.next(gameEvent);
});

return () => {
window.api.removeAllListeners('game:event');
};
}, [logger, gameEventsSubject$]);

// TODO don't scroll to bottom if user has scrolled up
// https://stackoverflow.com/questions/37620694/how-to-scroll-to-bottom-in-react
const scrollBottomRef = useRef<HTMLSpanElement>(null);
useEffect(() => {
if (scrollBottomRef.current) {
scrollBottomRef.current.scrollIntoView();
}
}, [gameText]);

const accountName = 'xxx';
const accountPassword = 'xxx';
const characterName = 'xxx';
Expand Down Expand Up @@ -249,6 +363,14 @@ const HomePage: React.FC = (): ReactNode => {
</EuiPanel>
);
})}
{gameText.map((text, index) => {
return (
<span key={index} style={{ fontFamily: 'Verdana' }}>
<span dangerouslySetInnerHTML={{ __html: text }} />
</span>
);
})}
<span ref={scrollBottomRef} />
</div>
);
};
Expand Down

0 comments on commit 501ae6c

Please sign in to comment.