Skip to content

Commit

Permalink
make PDA work with constructors
Browse files Browse the repository at this point in the history
  • Loading branch information
minecrawler committed Dec 1, 2020
1 parent 8e87d4e commit 1bac088
Show file tree
Hide file tree
Showing 11 changed files with 57 additions and 54 deletions.
6 changes: 2 additions & 4 deletions README.md
Expand Up @@ -103,11 +103,9 @@ using the handler function. Single calls to `dispatch()` do not offer the benefi
class InitState extends State { _systems = [InitSystem] }
class RunState extends State { _systems = [GravitySystem] }
class PauseState extends State { _systems = [PauseSystem] }
const initState = new InitState();
const runState = new RunState();

world.dispatch(initState);
while (true) world.dispatch(runState);
world.dispatch(InitState);
world.run({ initialState: RunState });
```

## Update loop
Expand Down
10 changes: 5 additions & 5 deletions examples/pong/src/app/frame-transition-handlers.ts
Expand Up @@ -4,7 +4,7 @@ import {MenuState} from "../states/menu";

let lastTransition = Date.now();

export async function afterFrameStep(actions: ITransitionActions) {
export async function afterFrameHandler(actions: ITransitionActions) {
const gameStore = actions.getResource(GameStore);

if (gameStore.exit) {
Expand All @@ -17,9 +17,9 @@ export async function afterFrameStep(actions: ITransitionActions) {
gameStore.popState = false;
}

if (gameStore.pushState) {
await actions.pushState(gameStore.pushState);
gameStore.pushState = undefined;
if (gameStore.PushState) {
await actions.pushState(gameStore.PushState);
gameStore.PushState = undefined;
}

if (gameStore.backToMenu) {
Expand All @@ -31,7 +31,7 @@ export async function afterFrameStep(actions: ITransitionActions) {
}
}

export async function beforeFrameStep(actions: ITransitionActions) {
export async function beforeFrameHandler(actions: ITransitionActions) {
const gameStore = actions.getResource(GameStore);

{ // Update info
Expand Down
4 changes: 2 additions & 2 deletions examples/pong/src/app/game-store.ts
@@ -1,5 +1,5 @@
import { EKeyState } from '../systems/input'
import {IState} from "../ecs";
import {IState, TStateProto} from "../ecs";

export enum EMovement {
up,
Expand Down Expand Up @@ -39,5 +39,5 @@ export class GameStore {
pointsLeft = 0
pointsRight = 0
popState = false
pushState?: IState
PushState?: TStateProto
}
43 changes: 21 additions & 22 deletions examples/pong/src/main.ts
@@ -1,6 +1,6 @@
import {ECS, IWorld} from "./ecs";
import {PaddleSystem} from "./systems/paddle";
import {afterFrameStep, beforeFrameStep} from "./app/frame-transition-handlers";
import {afterFrameHandler, beforeFrameHandler} from "./app/frame-transition-handlers";
import {InputSystem} from "./systems/input";
import {GameStore} from "./app/game-store";
import {MenuState} from "./states/menu";
Expand All @@ -17,7 +17,7 @@ import {GameItem} from "./components/game-item";
import {ScoreBoardSystem} from "./systems/score-board";


const cleanup = async (_world: IWorld) => {
const cleanup = () => {
const canvasEle = document.querySelector('canvas');
if (!canvasEle) throw new Error('Could not find canvas element!');

Expand All @@ -30,8 +30,7 @@ const cleanup = async (_world: IWorld) => {
};

const createWorld = () => {
const ecs = new ECS();
return ecs
return new ECS()
.buildWorld()
.withSystem(BallSystem, [InputSystem])
.withSystem(InputSystem)
Expand All @@ -40,18 +39,20 @@ const createWorld = () => {
.withSystem(PauseSystem, [InputSystem])
.withSystem(ScoreBoardSystem) // todo: throw if a system was not registered but is supposed to run
.withSystem(ScoreSystem, [InputSystem])
.withComponent(Ball)
.withComponent(GameItem)
.withComponent(MenuItem)
.withComponent(Paddle)
.withComponent(PauseItem)
.withComponent(Position)
.withComponent(ScoreItem)
.withComponent(UIItem)
.withComponents([
Ball,
GameItem,
MenuItem,
Paddle,
PauseItem,
Position,
ScoreItem,
UIItem,
])
.build();
};

const initGame = async (world: IWorld) => {
const initGame = (world: IWorld) => {
const canvasEle = document.querySelector('canvas');
if (!canvasEle) throw new Error('Could not find canvas element!');

Expand All @@ -71,21 +72,19 @@ const initGame = async (world: IWorld) => {
world.addResource(gameStore);
};

const runGame = async (world: IWorld) => {
const runGame = (world: IWorld) => {
return world.run({
afterStepHandler: afterFrameStep,
beforeStepHandler: beforeFrameStep,
afterStepHandler: afterFrameHandler,
beforeStepHandler: beforeFrameHandler,
initialState: MenuState,
}).catch(console.error);
});
}

// main function
(async () => {
const world = createWorld();

await initGame(world);
initGame(world);
await runGame(world);
await cleanup(world);
})();

// todo: find a way to fix the problem of optimizers destroying constructor names
cleanup();
})().catch(console.error);
2 changes: 1 addition & 1 deletion examples/pong/src/systems/menu.ts
Expand Up @@ -32,7 +32,7 @@ export class MenuSystem extends System<Data> {

if (this.gameStore.input.actions.menuConfirm) {
if (this.menuAction == EActions.Play) {
this.gameStore.pushState = new GameState();
this.gameStore.PushState = GameState;
}
else {
this.gameStore.exit = true;
Expand Down
2 changes: 1 addition & 1 deletion examples/pong/src/systems/pause.ts
Expand Up @@ -34,7 +34,7 @@ export class PauseSystem extends System<Data> {

if (this.gameStore.input.actions.togglePause) {
if (isGameState) {
this.gameStore.pushState = new PauseState();
this.gameStore.PushState = PauseState;
}
else {
this.gameStore.popState = true;
Expand Down
2 changes: 1 addition & 1 deletion examples/pong/src/systems/score.ts
Expand Up @@ -29,7 +29,7 @@ export class ScoreSystem extends System<Data> {
scorePoint(scoreItem: GameItem, score: number, ball: Ball, ballPos: Position, ballDir: Direction) {
scoreItem.score = score;
if (score == this.gameStore.pointLimit) {
this.gameStore.pushState = new ScoreBoardState();
this.gameStore.PushState = ScoreBoardState;
}

ballPos.x = this.canvasEle.width / 2 - ball.size / 2;
Expand Down
8 changes: 5 additions & 3 deletions src/pda.spec.ts
@@ -1,10 +1,12 @@
// todo: this PushDownAutomaton could get its own package on npm
export interface IPushDownAutomaton<T> {
import {TTypeProto} from "./_.spec";

export interface IPushDownAutomaton<T, P extends TTypeProto<T>> {
readonly state?: T
clear(): void
pop(): T | undefined
push(state: T): void
push(state: P): void
}

export type TPushDownAutomatonProto<T> = { new(): IPushDownAutomaton<T> };
export type TPushDownAutomatonProto<T, P> = { new(): IPushDownAutomaton<T, TTypeProto<T>> };
export default IPushDownAutomaton;
9 changes: 5 additions & 4 deletions src/pda.ts
@@ -1,11 +1,12 @@
import IPushDownAutomaton from "./pda.spec";
import {TTypeProto} from "./_.spec";

type TStateNode<T> = {
state: T,
prevNode?: TStateNode<T>,
};

export class PushDownAutomaton<T> implements IPushDownAutomaton<T> {
export class PushDownAutomaton<T, P extends TTypeProto<T>> implements IPushDownAutomaton<T, P> {
protected currentState?: T;
protected statesTail?: TStateNode<T>;

Expand All @@ -29,11 +30,11 @@ export class PushDownAutomaton<T> implements IPushDownAutomaton<T> {
return oldTail.state;
}

push(state: T): void {
this.currentState = state;
push(State: P): void {
this.currentState = new State();
this.statesTail = {
prevNode: this.statesTail,
state,
state: this.currentState,
};
}
}
6 changes: 3 additions & 3 deletions src/world.spec.ts
Expand Up @@ -24,7 +24,7 @@ export interface IStaticRunConfiguration {
afterStepHandler: (actions: ITransitionActions) => Promise<void> | void
beforeStepHandler: (actions: ITransitionActions) => Promise<void> | void
executionFunction: TExecutionFunction
initialState: IState,
initialState: TStateProto,
}
export type TSystemInfo<D extends TSystemData> = {
dataPrototype: TTypeProto<D>
Expand Down Expand Up @@ -170,9 +170,9 @@ export interface ITransitionActions extends IPartialWorld {

/**
* Change the running world to a new state
* @param newState
* @param NewState
*/
pushState(newState: IState): Promise<void>
pushState(NewState: TStateProto): Promise<void>
}

export interface IWorld extends IPartialWorld {
Expand Down
19 changes: 11 additions & 8 deletions src/world.ts
Expand Up @@ -30,14 +30,14 @@ export class World implements IWorld {
protected dirty = false;
protected entityInfos: Map<IEntity, TEntityInfo> = new Map();
protected entityWorld: IEntityWorld;
protected pda = new PushDownAutomaton<IState>();
protected pda = new PushDownAutomaton<IState, TStateProto>();
protected prefabs = {
nextHandle: 0,
entityLinks: new Map<number, IEntity[]>(),
};
protected resources = new Map<{ new(): Object }, Object>();
protected runExecutionPipeline: Set<TSystemInfo<TSystemData>>[] = [];
protected runExecutionPipelineCache: Map<IState, Set<TSystemInfo<TSystemData>>[]> = new Map();
protected runExecutionPipelineCache: Map<TStateProto, Set<TSystemInfo<TSystemData>>[]> = new Map();
protected runPromise?: Promise<void> = undefined;
protected _saveFormat?: ISaveFormat;
protected shouldRunSystems = false;
Expand Down Expand Up @@ -316,7 +316,7 @@ export class World implements IWorld {
}

await newState.activate(this.transitionWorld);
this.runExecutionPipeline = this.runExecutionPipelineCache.get(newState) ?? [];
this.runExecutionPipeline = this.runExecutionPipelineCache.get(newState.constructor as TStateProto) ?? [];
}

// todo: improve logic which sets up the groups (tracked by #13)
Expand Down Expand Up @@ -356,15 +356,18 @@ export class World implements IWorld {
return result;
}

protected async pushState(newState: IState): Promise<void> {
protected async pushState(NewState: TStateProto): Promise<void> {
await this.pda.state?.deactivate(this.transitionWorld);
this.pda.push(newState);
if (this.runExecutionPipelineCache.has(newState)) {
this.runExecutionPipeline = this.runExecutionPipelineCache.get(newState) ?? [];
this.pda.push(NewState);

const newState = this.pda.state!;

if (this.runExecutionPipelineCache.has(NewState)) {
this.runExecutionPipeline = this.runExecutionPipelineCache.get(NewState) ?? [];
} else {
newState.create(this.transitionWorld);
this.runExecutionPipeline = this.prepareExecutionPipeline(newState);
this.runExecutionPipelineCache.set(newState, this.runExecutionPipeline);
this.runExecutionPipelineCache.set(NewState, this.runExecutionPipeline);
}

await newState.activate(this.transitionWorld);
Expand Down

0 comments on commit 1bac088

Please sign in to comment.