Skip to content

Commit

Permalink
feat: evaluate map async with worker
Browse files Browse the repository at this point in the history
  • Loading branch information
anan474 committed May 24, 2024
1 parent 98b195d commit b908a3f
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 14 deletions.
24 changes: 22 additions & 2 deletions apollo/Types.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,40 @@
import Building, { PlainBuilding } from '@deities/athena/map/Building.tsx';
import { PlainEntitiesList } from '@deities/athena/map/PlainMap.tsx';
import { PlainEntitiesList, PlainMap } from '@deities/athena/map/PlainMap.tsx';
import Unit, { PlainUnit } from '@deities/athena/map/Unit.tsx';
import Vector from '@deities/athena/map/Vector.tsx';
import MapData from '@deities/athena/MapData.tsx';
import ImmutableMap from '@nkzw/immutable-map';
import { ActionResponse } from './ActionResponse.tsx';
import { Effects } from './Effects.tsx';
import { EncodedActionResponse } from './EncodedActions.tsx';
import {
decodeActionResponse,
encodeActionResponse,
EncodedActionResponse,
} from './EncodedActions.tsx';

export type GameStateEntry = readonly [ActionResponse, MapData];
export type EncodedGameStateEntry = readonly [EncodedActionResponse, PlainMap];
export type GameState = ReadonlyArray<GameStateEntry>;
export type EncodedGameState = ReadonlyArray<EncodedGameStateEntry>;
export type MutableGameState = Array<GameStateEntry>;
export type GameStateWithEffects = ReadonlyArray<
readonly [...GameStateEntry, Effects]
>;

export function encodeGameState(gameState: GameState): EncodedGameState {
return [...gameState].map(([actionResponse, map]) => [
encodeActionResponse(actionResponse),
map.toJSON(),
]);
}

export function decodeGameState(encodedGameState: EncodedGameState): GameState {
return encodedGameState.map(([encodedActionResponse, plainMap]) => [
decodeActionResponse(encodedActionResponse),
MapData.fromObject(plainMap),
]);
}

export type EncodedGameActionResponseWithError =
| EncodedGameActionResponse
// Error
Expand Down
3 changes: 3 additions & 0 deletions docs/vocs.config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,5 +155,8 @@ export default defineConfig({
resolve: {
alias: [resolver],
},
server: {
hmr: false,
},
},
});
34 changes: 34 additions & 0 deletions hera/editor/workers/evaluation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ActionResponse } from '@deities/apollo/ActionResponse.tsx';
import executeGameAction from '@deities/apollo/actions/executeGameAction.tsx';
import {
decodeEffects,
Effects,
encodeEffects,
} from '@deities/apollo/Effects.tsx';
import { encodeActionResponse } from '@deities/apollo/EncodedActions.tsx';
import { encodeGameState, GameState } from '@deities/apollo/Types.tsx';
import MapData from '@deities/athena/MapData.tsx';
import AIRegistry from '@deities/dionysus/AIRegistry.tsx';

onmessage = function (event) {
const [plainMap, encodedEffects, action, mutateAction] = event.data;

const map = MapData.fromObject(plainMap);
const currentViewer = map.getCurrentPlayer();
const vision = map.createVisionObject(currentViewer);
const effects = decodeEffects(encodedEffects);

const [actionResponse, initialActiveMap, gameState, newEffects]: [
ActionResponse | null,
MapData | null,
GameState | null,
Effects | null,
] = executeGameAction(map, vision, effects, action, AIRegistry, mutateAction);

postMessage([
actionResponse ? encodeActionResponse(actionResponse) : null,
initialActiveMap ? initialActiveMap?.toJSON() : null,
gameState ? encodeGameState(gameState) : null,
newEffects ? encodeEffects(newEffects) : null,
]);
};
52 changes: 40 additions & 12 deletions hera/hooks/useClientGameAction.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,37 @@
import { Action, MutateActionResponseFn } from '@deities/apollo/Action.tsx';
import { ActionResponse } from '@deities/apollo/ActionResponse.tsx';
import encodeGameActionResponse from '@deities/apollo/actions/encodeGameActionResponse.tsx';
import executeGameAction from '@deities/apollo/actions/executeGameAction.tsx';
import { Effects } from '@deities/apollo/Effects.tsx';
import {
decodeEffects,
Effects,
EncodedEffects,
encodeEffects,
} from '@deities/apollo/Effects.tsx';
import {
decodeActionResponse,
EncodedActionResponse,
} from '@deities/apollo/EncodedActions.tsx';
import { computeVisibleEndTurnActionResponse } from '@deities/apollo/lib/computeVisibleActions.tsx';
import decodeGameActionResponse from '@deities/apollo/lib/decodeGameActionResponse.tsx';
import dropLabelsFromActionResponse from '@deities/apollo/lib/dropLabelsFromActionResponse.tsx';
import dropLabelsFromGameState from '@deities/apollo/lib/dropLabelsFromGameState.tsx';
import { GameActionResponse, GameState } from '@deities/apollo/Types.tsx';
import {
decodeGameState,
EncodedGameState,
GameActionResponse,
GameState,
} from '@deities/apollo/Types.tsx';
import { PlainMap } from '@deities/athena/map/PlainMap.tsx';
import MapData from '@deities/athena/MapData.tsx';
import { getHiddenLabels } from '@deities/athena/WinConditions.tsx';
import AIRegistry from '@deities/dionysus/AIRegistry.tsx';
import onGameEnd from '@deities/hermes/game/onGameEnd.tsx';
import toClientGame, {
ClientGame,
} from '@deities/hermes/game/toClientGame.tsx';
import { useCallback } from 'react';
import EvaluationWorker from '../editor/workers/evaluation?worker';

var worker = new EvaluationWorker();

const ActionError = (action: Action) =>
new Error(`Map: Error executing remote '${action.type}' action.`);
Expand All @@ -26,7 +42,7 @@ export default function useClientGameAction(
mutateAction?: MutateActionResponseFn,
) {
return useCallback(
(action: Action): Promise<GameActionResponse> => {
async (action: Action): Promise<GameActionResponse> => {
if (!game) {
return Promise.reject(new Error('Client Game: Map state is missing.'));
}
Expand All @@ -46,15 +62,27 @@ export default function useClientGameAction(
}

try {
[actionResponse, initialActiveMap, gameState, newEffects] =
executeGameAction(
map,
vision,
game.effects,
const [
encodedActionResponse,
plainMap,
encodedGameState,
encodedEffects,
] = await new Promise<
[EncodedActionResponse, PlainMap, EncodedGameState, EncodedEffects]
>((resolve) => {
worker.postMessage([
map.toJSON(),
encodeEffects(game.effects),
action,
AIRegistry,
mutateAction,
) || [null, null, null];
]);
worker.onmessage = (event) => resolve(event.data);
});

actionResponse = decodeActionResponse(encodedActionResponse);
initialActiveMap = MapData.fromObject(plainMap);
gameState = decodeGameState(encodedGameState);
newEffects = decodeEffects(encodedEffects);
} catch (error) {
return Promise.reject(
process.env.NODE_ENV === 'development' ? error : ActionError(action),
Expand Down

0 comments on commit b908a3f

Please sign in to comment.