Skip to content

Commit

Permalink
simple save/load
Browse files Browse the repository at this point in the history
  • Loading branch information
patreeceeo committed Nov 10, 2023
1 parent 23bd323 commit 431674e
Show file tree
Hide file tree
Showing 17 changed files with 255 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
initializePhysicsSystem,
} from "./systems/PhysicsSystem";
import { setPixiAppId } from "./components/PixiAppId";
import { loadComponents } from "./functions/loadComponents";

if (module.hot) {
module.hot.accept((getParents) => {
Expand Down Expand Up @@ -79,6 +80,7 @@ export function startApp() {
addFrameRhythmCallback(() => {
TaskSwitcherSystem(TASK_MAP, TASK_CLEANUP_MAP);
});
loadComponents();
}

export function stopApp() {
Expand Down
56 changes: 56 additions & 0 deletions src/Component.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import test, { Mock } from "node:test";
import { localStorage } from "./globals";
import { savePartialComponent, loadPartialComponent } from "./Component";
import assert from "node:assert";
import { executeFilterQuery, registerEntity } from "./Query";
import { peekNextEntityId } from "./Entity";

function testSave<T>(
key: string,
data: ReadonlyArray<T>,
selectedEntities: ReadonlyArray<number>,
expectedData: ReadonlyArray<T>,
) {
savePartialComponent(key, data, selectedEntities);
const setItemMock = (
localStorage.setItem as Mock<typeof Storage.prototype.setItem>
).mock;

const lastCall = setItemMock.calls[setItemMock.calls.length - 1];
assert(lastCall.arguments[0] === key);
assert(lastCall.arguments[1] === JSON.stringify(expectedData));
}

test("savePartialComponent", () => {
testSave("Color", [8, 6, 7, 5, 3], [2, 3, 4], [7, 5, 3]);
testSave("Mass", [8, 6, 7, 5, 3], [2, 3, 4], [7, 5, 3]);
});

function testLoad<T>(
key: string,
data: T[],
loadedData: ReadonlyArray<T>,
nextEntityId: number,
expectedData: ReadonlyArray<T>,
expectedQueryResult: ReadonlyArray<number>,
) {
const getItemMock = (
localStorage.getItem as Mock<typeof Storage.prototype.getItem>
).mock;
getItemMock.mockImplementation(() => JSON.stringify(loadedData));
loadPartialComponent(key, data, nextEntityId);

assert.deepEqual(data, expectedData);

for (let i = 0; i < data.length; i++) {
registerEntity(i);
}
const queryResult = executeFilterQuery((_id) => true, []);
assert.deepEqual(queryResult, expectedQueryResult);
assert.equal(peekNextEntityId(), expectedQueryResult.length);
}

test("loadPartialComponent", () => {
testLoad("Color", [], [7, 5, 3], 3, [, , , 7, 5, 3], [0, 1, 2, 3, 4, 5]);
testLoad("Mass", [], [7, 5, 3], 3, [, , , 7, 5, 3], [0, 1, 2, 3, 4, 5]);
});
35 changes: 35 additions & 0 deletions src/Component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { peekNextEntityId, setNextEntityId } from "./Entity";
import { registerEntity } from "./Query";
import { localStorage } from "./globals";

export function savePartialComponent<T>(
key: string,
data: ReadonlyArray<T>,
selectedEntities: ReadonlyArray<number>,
) {
const selectedData = selectedEntities.map((entity) => data[entity]);
const item = JSON.stringify(selectedData);
localStorage.setItem(key, item);
}

export function loadPartialComponent<T>(
key: string,
data: T[],
nextEntityId: number,
) {
const item = localStorage.getItem(key);
if (item) {
const selectedData = JSON.parse(item) as T[];
for (
let i = 0, entityId = nextEntityId;
i < selectedData.length;
i++, entityId++
) {
data[entityId] = selectedData[i];
registerEntity(entityId);
}
if (peekNextEntityId() < nextEntityId + selectedData.length) {
setNextEntityId(nextEntityId + selectedData.length);
}
}
}
8 changes: 8 additions & 0 deletions src/Entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ export function addEntity(): number {
return id;
}

export function peekNextEntityId(): number {
return _nextId;
}

export function setNextEntityId(id: number): void {
_nextId = id;
}

export enum EntityName {
DEFAULT_PIXI_APP = "DEFAULT_PIXI_APP",
FLOOR_IMAGE = "FLOOR_IMAGE",
Expand Down
1 change: 1 addition & 0 deletions src/Input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const enum Key {
l = "l",
r = "r",
w = "w",
W = "W",
p = "p",
c = "c",
Space = " ",
Expand Down
2 changes: 1 addition & 1 deletion src/Query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export type ComplexFilter<RestArgs extends Array<number>> = {
const ALL_ENTITIES: Array<number> = [];

export function registerEntity(entityId: number): void {
ALL_ENTITIES.push(entityId);
ALL_ENTITIES[entityId] = entityId;
}

export function executeFilterQuery(
Expand Down
11 changes: 11 additions & 0 deletions src/components/ActLike.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { loadPartialComponent, savePartialComponent } from "../Component";

export enum ActLike {
PLAYER,
PUSHABLE,
BARRIER,
EDITOR_CURSOR,
}

const STORAGE_KEY = "Component:ActLike";
const DATA: Array<ActLike> = [];

export function setActLike(entityId: number, value: ActLike) {
Expand All @@ -14,3 +17,11 @@ export function setActLike(entityId: number, value: ActLike) {
export function isActLike(entityId: number, value: ActLike): boolean {
return DATA[entityId] === value;
}

export function saveActLike(selectedEntities: ReadonlyArray<number>) {
savePartialComponent(STORAGE_KEY, DATA, selectedEntities);
}

export function loadActLike(nextEntityId: number) {
loadPartialComponent(STORAGE_KEY, DATA, nextEntityId);
}
10 changes: 10 additions & 0 deletions src/components/Layer.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { loadPartialComponent, savePartialComponent } from "../Component";
import { invariant } from "../Error";
import { setRenderStateDirty } from "../systems/RenderSystem";

Expand All @@ -7,6 +8,7 @@ export const enum Layer {
USER_INTERFACE,
}

const STORAGE_KEY = "Component:Layer";
const DATA: Array<Layer> = [];

export function setLayer(entityId: number, value: Layer) {
Expand All @@ -30,3 +32,11 @@ export function removeLayer(entityId: number): void {
setRenderStateDirty();
delete DATA[entityId];
}

export function saveLayer(selectedEntities: ReadonlyArray<number>) {
savePartialComponent(STORAGE_KEY, DATA, selectedEntities);
}

export function loadLayer(nextEntityId: number) {
loadPartialComponent(STORAGE_KEY, DATA, nextEntityId);
}
10 changes: 10 additions & 0 deletions src/components/LookLike.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { loadPartialComponent, savePartialComponent } from "../Component";
import { invariant } from "../Error";
import { setRenderStateDirty } from "../systems/RenderSystem";
import { hasImage } from "./Image";

const STORAGE_KEY = "Component:LookLike";
const DATA: Array<number> = [];

export function setLookLike(entityId: number, imageId: number) {
Expand All @@ -27,3 +29,11 @@ export function getLookLike(entityId: number): number {
);
return DATA[entityId];
}

export function saveLookLike(selectedEntities: ReadonlyArray<number>) {
savePartialComponent(STORAGE_KEY, DATA, selectedEntities);
}

export function loadLookLike(nextEntityId: number) {
loadPartialComponent(STORAGE_KEY, DATA, nextEntityId);
}
10 changes: 10 additions & 0 deletions src/components/PixiAppId.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { loadPartialComponent, savePartialComponent } from "../Component";
import { invariant } from "../Error";
import { setRenderStateDirty } from "../systems/RenderSystem";
import { hasPixiApp } from "./PixiApp";

const STORAGE_KEY = "Component:PixiAppId";
const DATA: Array<number> = [];

export function setPixiAppId(entityId: number, appId: number) {
Expand All @@ -23,3 +25,11 @@ export function getPixiAppId(entityId: number): number {
);
return DATA[entityId];
}

export function savePixiAppId(selectedEntities: ReadonlyArray<number>) {
savePartialComponent(STORAGE_KEY, DATA, selectedEntities);
}

export function loadPixiAppId(nextEntityId: number) {
loadPartialComponent(STORAGE_KEY, DATA, nextEntityId);
}
10 changes: 10 additions & 0 deletions src/components/PositionX.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { loadPartialComponent, savePartialComponent } from "../Component";
import { invariant } from "../Error";
import { setRenderStateDirty } from "../systems/RenderSystem";

const STORAGE_KEY = "Component:PositionX";
const DATA: Array<number> = [];

export function setPositionX(entityId: number, value: number) {
Expand Down Expand Up @@ -30,3 +32,11 @@ export function removePositionX(entityId: number): void {
setRenderStateDirty();
delete DATA[entityId];
}

export function savePositionX(selectedEntities: ReadonlyArray<number>) {
savePartialComponent(STORAGE_KEY, DATA, selectedEntities);
}

export function loadPositionX(nextEntityId: number) {
loadPartialComponent(STORAGE_KEY, DATA, nextEntityId);
}
10 changes: 10 additions & 0 deletions src/components/PositionY.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { loadPartialComponent, savePartialComponent } from "../Component";
import { invariant } from "../Error";
import { setRenderStateDirty } from "../systems/RenderSystem";

const STORAGE_KEY = "Component:PositionY";
const DATA: Array<number> = [];

export function setPositionY(entityId: number, value: number) {
Expand Down Expand Up @@ -30,3 +32,11 @@ export function removePositionY(entityId: number): void {
setRenderStateDirty();
delete DATA[entityId];
}

export function savePositionY(selectedEntities: ReadonlyArray<number>) {
savePartialComponent(STORAGE_KEY, DATA, selectedEntities);
}

export function loadPositionY(nextEntityId: number) {
loadPartialComponent(STORAGE_KEY, DATA, nextEntityId);
}
28 changes: 28 additions & 0 deletions src/components/ShouldSave.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { loadPartialComponent, savePartialComponent } from "../Component";
import { invariant } from "../Error";
const STORAGE_KEY = "Component:ShouldSave";
const DATA: Array<boolean> = [];

export function setShouldSave(entityId: number, value: boolean) {
DATA[entityId] = value;
}

export function hasShouldSave(entityId: number): boolean {
return DATA[entityId] !== undefined;
}

export function getShouldSave(entityId: number): boolean {
invariant(
hasShouldSave(entityId),
`Entity ${entityId} does not have a ShouldSave`,
);
return DATA[entityId];
}

export function saveShouldSave(selectedEntities: ReadonlyArray<number>) {
savePartialComponent(STORAGE_KEY, DATA, selectedEntities);
}

export function loadShouldSave(nextEntityId: number) {
loadPartialComponent(STORAGE_KEY, DATA, nextEntityId);
}
19 changes: 19 additions & 0 deletions src/functions/loadComponents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { peekNextEntityId } from "../Entity";
import { loadActLike } from "../components/ActLike";
import { loadLayer } from "../components/Layer";
import { loadLookLike } from "../components/LookLike";
import { loadPixiAppId } from "../components/PixiAppId";
import { loadPositionX } from "../components/PositionX";
import { loadPositionY } from "../components/PositionY";
import { loadShouldSave } from "../components/ShouldSave";

export function loadComponents() {
const nextEntityId = peekNextEntityId();
loadShouldSave(nextEntityId);
loadLayer(nextEntityId);
loadPositionX(nextEntityId);
loadPositionY(nextEntityId);
loadLookLike(nextEntityId);
loadActLike(nextEntityId);
loadPixiAppId(nextEntityId);
}
25 changes: 25 additions & 0 deletions src/functions/saveComponents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { executeFilterQuery } from "../Query";
import { saveActLike } from "../components/ActLike";
import { saveLayer } from "../components/Layer";
import { saveLookLike } from "../components/LookLike";
import { savePixiAppId } from "../components/PixiAppId";
import { savePositionX } from "../components/PositionX";
import { savePositionY } from "../components/PositionY";
import { hasShouldSave, saveShouldSave } from "../components/ShouldSave";

const entityIds: number[] = [];
function getSelectedEntities(): ReadonlyArray<number> {
entityIds.length = 0;
return executeFilterQuery(hasShouldSave, entityIds);
}

export function saveComponents() {
const selectedEntities = getSelectedEntities();
saveShouldSave(selectedEntities);
saveLayer(selectedEntities);
savePositionX(selectedEntities);
savePositionY(selectedEntities);
saveLookLike(selectedEntities);
saveActLike(selectedEntities);
savePixiAppId(selectedEntities);
}
12 changes: 12 additions & 0 deletions src/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,15 @@ export const clearInterval =
process.env.NODE_ENV === "test"
? test.mock.fn(clearIntervalMock)
: globalThis.clearInterval;

export const localStorage: Storage =
process.env.NODE_ENV === "test"
? {
getItem: test.mock.fn(),
setItem: test.mock.fn(),
removeItem: test.mock.fn(),
clear: test.mock.fn(),
key: test.mock.fn(),
length: 0,
}
: globalThis.localStorage;
Loading

0 comments on commit 431674e

Please sign in to comment.