Skip to content

Commit 862d78c

Browse files
committed
feat(engine): implement type-safe GameInstance.context
1 parent b8b2b30 commit 862d78c

File tree

18 files changed

+93
-83
lines changed

18 files changed

+93
-83
lines changed

.changeset/dirty-horses-rhyme.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@jolly-pixel/engine": minor
3+
---
4+
5+
Implement type-safe GameInstance.context

packages/engine/src/actor/Actor.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,36 @@ import type {
1111
Component
1212
} from "../components/types.ts";
1313

14-
type ComponentConstructor = new (actor: Actor, ...args: any[]) => Component;
14+
type ComponentConstructor = new (actor: Actor<any>, ...args: any[]) => Component;
1515

1616
type RequiresOptions<T extends ComponentConstructor> =
17-
T extends new (actor: Actor, options: infer O, ...args: any[]) => any
17+
T extends new (actor: Actor<any>, options: infer O, ...args: any[]) => any
1818
? undefined extends O ? false : true
1919
: false;
2020

21-
export interface ActorOptions {
21+
export interface ActorOptions<TContext = Record<string, unknown>> {
2222
name: string;
23-
parent?: Actor | null;
23+
parent?: Actor<TContext> | null;
2424
visible?: boolean;
2525
layer?: number | number[];
2626
}
2727

28-
export class Actor extends ActorTree {
29-
gameInstance: GameInstance;
28+
export class Actor<TContext = Record<string, unknown>> extends ActorTree<TContext> {
29+
gameInstance: GameInstance<any, TContext>;
3030

3131
name: string;
3232
awoken = false;
33-
parent: Actor | null = null;
33+
parent: Actor<TContext> | null = null;
3434
components: Component[] = [];
35-
behaviors: Record<string, Behavior<any>[]> = {};
35+
behaviors: Record<string, Behavior<any, TContext>[]> = {};
3636
transform: Transform;
3737
pendingForDestruction = false;
3838

3939
threeObject = new THREE.Group();
4040

4141
constructor(
42-
gameInstance: GameInstance<any>,
43-
options: ActorOptions
42+
gameInstance: GameInstance<any, any>,
43+
options: ActorOptions<TContext>
4444
) {
4545
super();
4646
const { name, parent = null, visible = true, layer } = options;
@@ -110,8 +110,8 @@ export class Actor extends ActorTree {
110110
return component as InstanceType<T>;
111111
}
112112

113-
getComponentByName<T extends ActorComponent>(
114-
actor: Actor,
113+
getComponentByName<T extends ActorComponent<TContext>>(
114+
actor: Actor<TContext>,
115115
componentName: string
116116
): T {
117117
const component = actor.components.find(
@@ -161,7 +161,7 @@ export class Actor extends ActorTree {
161161
this.destroyAllActors();
162162
}
163163

164-
addBehavior<T extends new(...args: any) => Behavior>(
164+
addBehavior<T extends new(...args: any) => Behavior<any, TContext>>(
165165
behaviorClass: T,
166166
properties: ConstructorParameters<T>[0] = Object.create(null)
167167
) {
@@ -220,7 +220,7 @@ export class Actor extends ActorTree {
220220
}
221221

222222
setParent(
223-
newParent: Actor,
223+
newParent: Actor<TContext>,
224224
keepLocal = false
225225
) {
226226
if (this.pendingForDestruction) {

packages/engine/src/actor/ActorComponent.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ import type {
99
FreeComponentEnum
1010
} from "../components/types.ts";
1111

12-
export interface ActorComponentOptions {
13-
actor: Actor;
12+
export interface ActorComponentOptions<TContext = Record<string, unknown>> {
13+
actor: Actor<TContext>;
1414
typeName: FreeComponentEnum;
1515
}
1616

1717
export type ActorComponentEvents = {
1818
metadataInitialized: [];
1919
};
2020

21-
export class ActorComponent extends EventEmitter<ActorComponentEvents> implements Component {
21+
export class ActorComponent<TContext = Record<string, unknown>> extends EventEmitter<ActorComponentEvents> implements Component {
2222
protected static Id = 0;
2323

2424
static generateNextId() {
@@ -30,13 +30,13 @@ export class ActorComponent extends EventEmitter<ActorComponentEvents> implement
3030
}
3131

3232
id = ActorComponent.generateNextId();
33-
actor: Actor;
33+
actor: Actor<TContext>;
3434
typeName: FreeComponentEnum;
3535

3636
pendingForDestruction = false;
3737

3838
constructor(
39-
options: ActorComponentOptions
39+
options: ActorComponentOptions<TContext>
4040
) {
4141
super();
4242
this.actor = options.actor;

packages/engine/src/actor/ActorTree.ts

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,37 @@ import pm from "picomatch";
44
// Import Internal Dependencies
55
import { Actor } from "./Actor.ts";
66

7-
export type ActorTreeNode = {
8-
actor: Actor;
9-
parent?: Actor;
7+
export type ActorTreeNode<TContext = Record<string, unknown>> = {
8+
actor: Actor<TContext>;
9+
parent?: Actor<TContext>;
1010
};
1111

12-
export interface ActorTreeOptions {
13-
addCallback?: (actor: Actor) => void;
14-
removeCallback?: (actor: Actor) => void;
12+
export interface ActorTreeOptions<TContext = Record<string, unknown>> {
13+
addCallback?: (actor: Actor<TContext>) => void;
14+
removeCallback?: (actor: Actor<TContext>) => void;
1515
}
1616

17-
export class ActorTree {
18-
#addCallback?: (actor: Actor) => void;
19-
#removeCallback?: (actor: Actor) => void;
17+
export class ActorTree<TContext = Record<string, unknown>> {
18+
#addCallback?: (actor: Actor<TContext>) => void;
19+
#removeCallback?: (actor: Actor<TContext>) => void;
2020

21-
children: Actor[] = [];
21+
children: Actor<TContext>[] = [];
2222

2323
constructor(
24-
options: ActorTreeOptions = {}
24+
options: ActorTreeOptions<TContext> = {}
2525
) {
2626
this.#addCallback = options.addCallback;
2727
this.#removeCallback = options.removeCallback;
2828
}
2929

3030
add(
31-
actor: Actor
31+
actor: Actor<TContext>
3232
): void {
3333
this.children.push(actor);
3434
this.#addCallback?.(actor);
3535
}
3636

37-
remove(actor: Actor): void {
37+
remove(actor: Actor<TContext>): void {
3838
const index = this.children.indexOf(actor);
3939
if (index !== -1) {
4040
this.children.splice(index, 1);
@@ -44,7 +44,7 @@ export class ActorTree {
4444

4545
* getActors(
4646
pattern: string
47-
): IterableIterator<Actor> {
47+
): IterableIterator<Actor<TContext>> {
4848
if (pattern.includes("/")) {
4949
yield* this.#getActorsByPatternPath(pattern);
5050

@@ -62,7 +62,7 @@ export class ActorTree {
6262

6363
* #getActorsByPatternPath(
6464
pattern: string
65-
): IterableIterator<Actor> {
65+
): IterableIterator<Actor<TContext>> {
6666
const parts = pattern.split("/").filter((part) => part !== "");
6767

6868
for (const rootActor of this.children) {
@@ -73,10 +73,10 @@ export class ActorTree {
7373
}
7474

7575
* #matchActorPath(
76-
actor: Actor,
76+
actor: Actor<TContext>,
7777
patternParts: string[],
7878
patternIndex: number
79-
): IterableIterator<Actor> {
79+
): IterableIterator<Actor<TContext>> {
8080
if (patternIndex >= patternParts.length) {
8181
return;
8282
}
@@ -138,7 +138,7 @@ export class ActorTree {
138138
*/
139139
getActor(
140140
name: string
141-
): Actor | null {
141+
): Actor<TContext> | null {
142142
if (name.includes("/")) {
143143
return this.#getActorByPath(name);
144144
}
@@ -154,14 +154,14 @@ export class ActorTree {
154154

155155
#getActorByPath(
156156
path: string
157-
): Actor | null {
157+
): Actor<TContext> | null {
158158
const parts = path.split("/").filter((part) => part !== "");
159159
const parentNode = this.getActor(parts[0]);
160160
if (!parentNode) {
161161
return null;
162162
}
163163

164-
let currentNode: Actor | null = parentNode;
164+
let currentNode: Actor<TContext> | null = parentNode;
165165
for (let i = 1; i < parts.length; i++) {
166166
if (!currentNode) {
167167
break;
@@ -175,22 +175,22 @@ export class ActorTree {
175175
return currentNode;
176176
}
177177

178-
* getRootActors(): IterableIterator<Actor> {
178+
* getRootActors(): IterableIterator<Actor<TContext>> {
179179
for (const rootActor of this.children) {
180180
if (!rootActor.pendingForDestruction) {
181181
yield rootActor;
182182
}
183183
}
184184
}
185185

186-
* getAllActors(): IterableIterator<Actor> {
186+
* getAllActors(): IterableIterator<Actor<TContext>> {
187187
for (const { actor } of this.walk()) {
188188
yield actor;
189189
}
190190
}
191191

192192
destroyActor(
193-
actor: Actor
193+
actor: Actor<TContext>
194194
) {
195195
if (!actor.pendingForDestruction) {
196196
actor.markDestructionPending();
@@ -204,31 +204,31 @@ export class ActorTree {
204204
}
205205

206206
* #walkDepthFirstGenerator(
207-
node: Actor,
208-
parentNode?: Actor
209-
): IterableIterator<ActorTreeNode> {
207+
node: Actor<TContext>,
208+
parentNode?: Actor<TContext>
209+
): IterableIterator<ActorTreeNode<TContext>> {
210210
yield { actor: node, parent: parentNode };
211211

212212
for (const child of node.children) {
213213
yield* this.#walkDepthFirstGenerator(child, node);
214214
}
215215
}
216216

217-
* walk(): IterableIterator<ActorTreeNode> {
217+
* walk(): IterableIterator<ActorTreeNode<TContext>> {
218218
for (const child of this.children) {
219219
yield* this.#walkDepthFirstGenerator(child, undefined);
220220
}
221221
}
222222

223223
* walkFromNode(
224-
rootNode: Actor
225-
): IterableIterator<ActorTreeNode> {
224+
rootNode: Actor<TContext>
225+
): IterableIterator<ActorTreeNode<TContext>> {
226226
for (const child of rootNode.children) {
227227
yield* this.#walkDepthFirstGenerator(child, rootNode);
228228
}
229229
}
230230

231-
* [Symbol.iterator](): IterableIterator<Actor> {
231+
* [Symbol.iterator](): IterableIterator<Actor<TContext>> {
232232
for (const actor of this.children) {
233233
if (!actor.pendingForDestruction) {
234234
yield actor;

packages/engine/src/adapters/global.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import {
44
} from "../systems/GameInstance.ts";
55

66
export interface GlobalsAdapter {
7-
setGame(instance: GameInstance<any>): void;
7+
setGame(instance: GameInstance<any, any>): void;
88
}
99

1010
export class BrowserGlobalsAdapter implements GlobalsAdapter {
1111
setGame(
12-
instance: GameInstance<any>
12+
instance: GameInstance<any, any>
1313
) {
1414
globalThis.game = instance;
1515
}

packages/engine/src/components/camera/Camera3DControls.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class Camera3DControls extends Behavior {
3333
#movementSpeed: number;
3434

3535
constructor(
36-
actor: Actor,
36+
actor: Actor<any>,
3737
options: Camera3DControlsOptions = {}
3838
) {
3939
super(actor);

packages/engine/src/components/renderers/model/ModelRenderer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ export interface ModelRendererOptions {
2525
animations?: ModelRendererAnimationOptions;
2626
}
2727

28-
export class ModelRenderer extends ActorComponent {
28+
export class ModelRenderer extends ActorComponent<any> {
2929
#asset: Systems.LazyAsset<Model>;
3030
#object: THREE.Group<THREE.Object3DEventMap>;
3131
#debug = false;
3232

3333
animation = new ModelAnimation();
3434

3535
constructor(
36-
actor: Actor,
36+
actor: Actor<any>,
3737
options: ModelRendererOptions
3838
) {
3939
super({

packages/engine/src/components/renderers/sprite/SpriteRenderer.class.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export interface SpriteRendererOptions {
1919
};
2020
}
2121

22-
export class SpriteRenderer extends ActorComponent {
22+
export class SpriteRenderer extends ActorComponent<any> {
2323
frameIndex: number;
2424
tileHorizontal: number;
2525
tileVertical: number;
@@ -33,7 +33,7 @@ export class SpriteRenderer extends ActorComponent {
3333
threeObject: THREE.Sprite;
3434

3535
constructor(
36-
actor: Actor,
36+
actor: Actor<any>,
3737
options: SpriteRendererOptions
3838
) {
3939
super({

packages/engine/src/components/renderers/text/TextRenderer.class.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ export interface TextRendererOptions extends Omit<Text3DOptions, "font"> {
1515
text?: string;
1616
}
1717

18-
export class TextRenderer extends ActorComponent {
18+
export class TextRenderer extends ActorComponent<any> {
1919
#asset: Systems.LazyAsset<Font>;
2020

2121
text: Text3D;
2222

2323
constructor(
24-
actor: Actor,
24+
actor: Actor<any>,
2525
options: TextRendererOptions
2626
) {
2727
super({

packages/engine/src/components/renderers/tiled/TileMapRenderer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ export interface TiledMapRendererOptions {
1919
orientation?: TiledMapOrientation;
2020
}
2121

22-
export class TiledMapRenderer extends ActorComponent {
22+
export class TiledMapRenderer extends ActorComponent<any> {
2323
#map: Systems.LazyAsset<TiledMapAsset>;
2424
#orientation: TiledMapOrientation;
2525

2626
constructor(
27-
actor: Actor,
27+
actor: Actor<any>,
2828
options: TiledMapRendererOptions
2929
) {
3030
const {

0 commit comments

Comments
 (0)