Change typings to distinguish dense and sparse stores, and have the container add the "generation" field instead of the container

This commit is contained in:
Tangent Wantwight 2020-01-15 22:49:52 -05:00
parent e8415da145
commit 8434e7046c
5 changed files with 36 additions and 37 deletions

View file

@ -5,7 +5,8 @@
"private": true,
"scripts": {
"build": "webpack",
"buildProduction": "webpack --mode=production"
"buildProduction": "webpack --mode=production",
"tsc:check": "tsc --noEmit"
},
"author": "Tangent 128",
"license": "ISC",

View file

@ -1,5 +1,5 @@
import { Data as CoreData, Store } from "./Data";
import { Data as CoreData, Store, SparseStore } from "./Data";
import { Layer, SpriteSheet } from "../Applet/Render";
export class Box {
@ -68,8 +68,8 @@ export class RenderSprite {
export class Data extends CoreData {
location: Store<Location> = [];
bounds: Store<Polygon> = [];
renderBounds: Store<RenderBounds> = {};
renderSprite: Store<RenderSprite> = {};
collisionSourceClass: Store<CollisionClass> = {};
collisionTargetClass: Store<CollisionClass> = {};
renderBounds: SparseStore<RenderBounds> = {};
renderSprite: SparseStore<RenderSprite> = {};
collisionSourceClass: SparseStore<CollisionClass> = {};
collisionTargetClass: SparseStore<CollisionClass> = {};
}

View file

@ -1,25 +1,26 @@
export interface Component {
generation: number;
}
export type Id = [number, number];
export enum Liveness {
export const enum Liveness {
DEAD = 0,
ALIVE = 1,
INACTIVE = 2
}
export interface EntityState extends Component {
export interface HasGeneration {
generation: number;
}
export interface EntityState {
alive: Liveness;
}
export type Store<T> = (T & Component)[] | Record<number, T & Component>;
export type Store<T> = (T & HasGeneration)[];
export type SparseStore<T> = Record<number, T & HasGeneration>;
export class Data {
entity: EntityState[] = [];
entity: Store<EntityState> = [];
[name: string]: Store<{}>;
[name: string]: Store<{}> | SparseStore<{}>;
}
/**
@ -29,11 +30,8 @@ export class Data {
* @param state Liveness state, allows creating an inactive entity
* @returns the new entity's ID and generation
*/
type StripKeys<T, N> = {
[P in keyof T]: P extends N ? never : P
}[keyof T];
type Assigner<DATA extends Data> = {
[S in keyof DATA]?: Pick<DATA[S][number], StripKeys<DATA[S][number], "generation">>
[S in keyof DATA]?: Pick<DATA[S][number], Exclude<keyof DATA[S][number], "generation">>
};
export function Create<DATA extends Data>(data: DATA, assign: Assigner<DATA>, state = Liveness.ALIVE): Id {
const entities = data.entity;
@ -126,7 +124,7 @@ export function Lookup<
* @param components names of components to look for
* @returns the cooresponding components, with unfound ones replaced by nulls
*/
export function Lookup<DATA extends Data, K extends keyof DATA>(data: DATA, [id, generation]: Id, ...components: K[]): (Component | null)[] {
export function Lookup<DATA extends Data, K extends keyof DATA>(data: DATA, [id, generation]: Id, ...components: K[]): ({} | null)[] {
const entity = data.entity[id];
// inactive entities are fine to lookup, but dead ones are not
if(entity && entity.generation == generation && entity.alive != Liveness.DEAD) {
@ -206,11 +204,11 @@ export function Join<
* Query a Data collection for all Alive entities possessing the named set of Components.
* @returns an array of tuples containing the matching entity [ID, generation]s & associated Components
*/
export function Join<DATA extends Data, K extends keyof DATA>(data: DATA, ...components: K[]): [Id, ...Component[]][] {
export function Join<DATA extends Data, K extends keyof DATA>(data: DATA, ...components: K[]): [Id, ...{}[]][] {
const entities = data.entity;
const stores: Store<{}>[] = components.map(name => data[name]);
const stores: (Store<{}>|SparseStore<{}>)[] = components.map(name => data[name]);
const results: [Id, ...Component[]][] = [];
const results: [Id, ...{}[]][] = [];
const firstStore = stores[0];
if(Array.isArray(firstStore)) {
for(let id = 0; id < firstStore.length; id++) {
@ -223,8 +221,8 @@ export function Join<DATA extends Data, K extends keyof DATA>(data: DATA, ...com
}
return results;
}
function JoinLoop(id: number, entities: Store<EntityState>, stores: Store<{}>[], results: [Id, ...Component[]][]) {
const result: [Id, ...Component[]] = [[id, -1]];
function JoinLoop(id: number, entities: Store<EntityState>, stores: (Store<{}>|SparseStore<{}>)[], results: [Id, ...{}[]][]) {
const result: [Id, ...{}[]] = [[id, -1]];
let generation = -1;
for (const store of stores) {

View file

@ -4,15 +4,15 @@ import { Loop } from "../Applet/Loop";
import { Layer, DrawSet } from "../Applet/Render";
import { Data, Location, Polygon, RenderBounds, CollisionClass } from "./Components";
import { FindCollisions } from "./Collision";
import { Component, Join, Liveness, Remove, Create, Lookup } from "./Data";
import { Join, Liveness, Remove, Create, Lookup, Store, SparseStore } from "./Data";
import { DumbMotion } from "./Location";
import { RunRenderBounds } from "./Renderers";
interface Apple extends Component {}
interface Banana extends Component {
interface Apple {}
interface Banana {
peeled: boolean
}
interface Carrot extends Component {
interface Carrot {
cronch: number
}
@ -25,7 +25,7 @@ class TestData extends Data {
{generation: 5, alive: Liveness.INACTIVE},
{generation: 5, alive: Liveness.ALIVE},
];
apple: Apple[] = [
apple: Store<Apple> = [
{generation: 5},
{generation: 5},
{generation: -1},
@ -33,11 +33,11 @@ class TestData extends Data {
{generation: 5},
{generation: 5},
];
banana: Record<number, Banana> = {
banana: SparseStore<Banana> = {
3: {generation: 5, peeled: false},
4: {generation: 5, peeled: true},
};
carrot: Record<number, Carrot> = {
carrot: SparseStore<Carrot> = {
0: {generation: 5, cronch: 1},
1: {generation: 5, cronch: 1},
2: {generation: 4, cronch: 10},

View file

@ -1,5 +1,5 @@
import { Layer, DrawSet } from "../Applet/Render";
import { Store } from "../Ecs/Data";
import { SparseStore } from "../Ecs/Data";
import { Data as EcsData } from "../Ecs/Components";
export enum GamePhase {
@ -54,11 +54,11 @@ export class World {
}
export class Data extends EcsData {
boss: Store<Boss> = {};
bullet: Store<Bullet> = {};
hp: Store<Hp> = {};
lifetime: Store<Lifetime> = {};
message: Store<Message> = {};
boss: SparseStore<Boss> = {};
bullet: SparseStore<Bullet> = {};
hp: SparseStore<Hp> = {};
lifetime: SparseStore<Lifetime> = {};
message: SparseStore<Message> = {};
}
export enum Teams {