mani-game-engine 1.0.0-pre.4 → 1.0.0-pre.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +69 -69
- package/lib/clock.d.ts +55 -41
- package/lib/clock.js +117 -77
- package/lib/clock.js.map +1 -1
- package/lib/ecsInjector.d.ts +46 -21
- package/lib/ecsInjector.js +218 -124
- package/lib/ecsInjector.js.map +1 -1
- package/lib/entity.d.ts +32 -22
- package/lib/entity.js +159 -83
- package/lib/entity.js.map +1 -1
- package/lib/gameEngine.d.ts +89 -66
- package/lib/gameEngine.js +361 -272
- package/lib/gameEngine.js.map +1 -1
- package/lib/index.d.ts +12 -6
- package/lib/index.js +35 -5
- package/lib/index.js.map +1 -1
- package/lib/injector.d.ts +121 -0
- package/lib/injector.js +290 -0
- package/lib/injector.js.map +1 -0
- package/lib/scope/scopeContext.d.ts +69 -0
- package/lib/scope/scopeContext.js +282 -0
- package/lib/scope/scopeContext.js.map +1 -0
- package/lib/systemContext.d.ts +14 -0
- package/lib/systemContext.js +36 -0
- package/lib/systemContext.js.map +1 -0
- package/lib/types.d.ts +80 -54
- package/lib/types.js +19 -0
- package/lib/types.js.map +1 -1
- package/lib/utils/map2k.d.ts +6 -6
- package/lib/utils/map2k.js +43 -39
- package/lib/utils/map2k.js.map +1 -1
- package/package.json +39 -45
- package/src/clock.ts +163 -102
- package/src/ecsInjector.ts +274 -164
- package/src/entity.ts +190 -113
- package/src/gameEngine.ts +463 -350
- package/src/index.ts +32 -9
- package/src/injector.ts +364 -0
- package/src/scope/scopeContext.ts +359 -0
- package/src/systemContext.ts +38 -0
- package/src/types.ts +95 -70
- package/src/utils/map2k.ts +52 -52
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import {Class, EcsInjector, Signal, SignalBinding, SignalCallback} from '../index';
|
|
2
|
+
import {signalHandlers} from '../ecsInjector';
|
|
3
|
+
import {ID, putIfAbsent} from '../injector';
|
|
4
|
+
|
|
5
|
+
export type ScopeSignalOptions = { keepAlive?: boolean | Scope[], group?: string }
|
|
6
|
+
|
|
7
|
+
export const scopeSignalHandlers = new Map<Object, [string, ID, ScopeSignalOptions?][]>();
|
|
8
|
+
|
|
9
|
+
export const OnScopeSignal = (id: ID, options?: ScopeSignalOptions) => (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
|
|
10
|
+
if (target instanceof Function) {
|
|
11
|
+
throw new Error('only allowed on non static methods');
|
|
12
|
+
} else {
|
|
13
|
+
const mappingList = putIfAbsent(scopeSignalHandlers, target.constructor, (): [string, ID, ScopeSignalOptions?][] => []);
|
|
14
|
+
mappingList.push([propertyKey, id, options]);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const SCOPE_CONTEXT = {
|
|
19
|
+
log: true,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export interface Scope {
|
|
23
|
+
[propName: string]: any; // disable weak type detection
|
|
24
|
+
onEnter?(): void;
|
|
25
|
+
onExit?(): void;
|
|
26
|
+
onSubReturn?(): void;
|
|
27
|
+
onSubExit?(): void;
|
|
28
|
+
onActivate?(): void;
|
|
29
|
+
onDeactivate?(): void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class ScopeSignalBinding {
|
|
33
|
+
readonly keepAlive?: boolean | Scope[];
|
|
34
|
+
private group?: string;
|
|
35
|
+
|
|
36
|
+
constructor(private signalBinding: SignalBinding, options?: ScopeSignalOptions) {
|
|
37
|
+
this.keepAlive = options?.keepAlive;
|
|
38
|
+
this.group = options?.group;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
activate() {
|
|
42
|
+
this.signalBinding.setActive(true);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
deactivate() {
|
|
46
|
+
this.signalBinding.setActive(false);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
detach() {
|
|
50
|
+
this.signalBinding.detach();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
class ScopeStack {
|
|
55
|
+
readonly stack: ScopeContext[] = [];
|
|
56
|
+
queuedScopeChanges: Function[] = [];
|
|
57
|
+
ongoingChange = false;
|
|
58
|
+
target?: Scope;
|
|
59
|
+
|
|
60
|
+
constructor(readonly rootScope: ScopeContext) {
|
|
61
|
+
this.stack.push(rootScope);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get rootContext() {
|
|
65
|
+
return this.stack[0];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get activeContext() {
|
|
69
|
+
return this.stack[this.stack.length - 1];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type ScopeMapping = (params: {
|
|
75
|
+
injector: EcsInjector,
|
|
76
|
+
registerScopeService: (serviceClass: Class) => void,
|
|
77
|
+
onEnter: Signal<ScopeContext>,
|
|
78
|
+
onExit: Signal<ScopeContext>,
|
|
79
|
+
onSubReturn: Signal<ScopeContext>,
|
|
80
|
+
onSubExit: Signal<ScopeContext>,
|
|
81
|
+
onActivate: Signal<ScopeContext>,
|
|
82
|
+
onDeactivate: Signal<ScopeContext>,
|
|
83
|
+
|
|
84
|
+
}) => void
|
|
85
|
+
|
|
86
|
+
interface AddScopeSignalOptions extends ScopeSignalOptions {
|
|
87
|
+
context?: unknown;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// TODO: maybe move Signal resolver to Injector and dont use EcsInjector here?
|
|
91
|
+
export class ScopeContext {
|
|
92
|
+
readonly scope: Scope;
|
|
93
|
+
readonly injector: EcsInjector;
|
|
94
|
+
private readonly stack: ScopeStack; // TODO: naming
|
|
95
|
+
private readonly signalBindings: ScopeSignalBinding[] = [];
|
|
96
|
+
|
|
97
|
+
// Scope services will be active (signals bounds) in this scope and all sub scopes
|
|
98
|
+
// when this scope exits, the signals will be unbound
|
|
99
|
+
private serviceBindings: SignalBinding[] = [];
|
|
100
|
+
private closed = false;
|
|
101
|
+
private muteKeepAliveSignals = false;
|
|
102
|
+
|
|
103
|
+
readonly onEnter = new Signal<ScopeContext>();
|
|
104
|
+
readonly onExit = new Signal<ScopeContext>();
|
|
105
|
+
readonly onSubReturn = new Signal<ScopeContext>();
|
|
106
|
+
readonly onSubExit = new Signal<ScopeContext>();
|
|
107
|
+
readonly onActivate = new Signal<ScopeContext>();
|
|
108
|
+
readonly onDeactivate = new Signal<ScopeContext>();
|
|
109
|
+
|
|
110
|
+
constructor(injector: EcsInjector, scopeClass: Class, mapping?: ScopeMapping, private parent?: ScopeContext) {
|
|
111
|
+
if (!parent) {
|
|
112
|
+
this.stack = new ScopeStack(this);
|
|
113
|
+
|
|
114
|
+
} else {
|
|
115
|
+
this.stack = parent.stack;
|
|
116
|
+
this.stack.stack.push(this);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// TODO: disable this for the rootscope?
|
|
120
|
+
this.injector = injector.createChild();
|
|
121
|
+
|
|
122
|
+
//TODO: i think it would really be better if we implement an injector.injectIntoUnmapped() instead of mapping here
|
|
123
|
+
this.injector.map(scopeClass).toSingleton();
|
|
124
|
+
this.injector.map(ScopeContext).toValue(this);
|
|
125
|
+
|
|
126
|
+
mapping?.({
|
|
127
|
+
injector: this.injector,
|
|
128
|
+
registerScopeService: serviceClass => {
|
|
129
|
+
this.injector.map(serviceClass).toSingleton();
|
|
130
|
+
this.mapServiceSignals(this.injector.get(serviceClass));
|
|
131
|
+
},
|
|
132
|
+
onEnter: this.onEnter,
|
|
133
|
+
onExit: this.onExit,
|
|
134
|
+
onSubReturn: this.onSubReturn,
|
|
135
|
+
onSubExit: this.onSubExit,
|
|
136
|
+
onActivate: this.onActivate,
|
|
137
|
+
onDeactivate: this.onDeactivate,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
this.scope = this.injector.get(scopeClass) as Scope;
|
|
141
|
+
this.mapScopeSignals();
|
|
142
|
+
this.updateSignalBindings();
|
|
143
|
+
// TODO: maybe the scope needs to prepare resources or something before it can react to signals, so we should move onEnter bevore the signal mapping and make it async (not working in constructor though)
|
|
144
|
+
this.scope.onEnter?.();
|
|
145
|
+
this.onEnter.dispatch(this);
|
|
146
|
+
this.scope.onActivate?.();
|
|
147
|
+
this.onActivate.dispatch(this);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
get isRoot() {
|
|
151
|
+
return this === this.stack.rootScope;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// TODO: test this
|
|
155
|
+
get target() {
|
|
156
|
+
return this.stack.target;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
get isActive() {
|
|
160
|
+
return this === this.stack.activeContext;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
get activeContext() { return this.stack.activeContext;}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* returns the new scope only if there is currently no ongoing scope change happening
|
|
167
|
+
* @param scopeClass
|
|
168
|
+
* @param mapping
|
|
169
|
+
*/
|
|
170
|
+
enterScope(scopeClass: Class, mapping?: ScopeMapping): ScopeContext | undefined {
|
|
171
|
+
let newContext;
|
|
172
|
+
const doChange = () => {
|
|
173
|
+
|
|
174
|
+
this.activeContext.scope.onSubExit?.();
|
|
175
|
+
this.activeContext.onSubExit.dispatch(this);
|
|
176
|
+
this.activeContext.scope.onDeactivate?.();
|
|
177
|
+
this.activeContext.onDeactivate.dispatch(this);
|
|
178
|
+
|
|
179
|
+
newContext = new ScopeContext(this.activeContext.injector, scopeClass, mapping, this.activeContext);
|
|
180
|
+
|
|
181
|
+
this.logStack();
|
|
182
|
+
const nextChange = this.stack.queuedScopeChanges.shift();
|
|
183
|
+
if (nextChange) {
|
|
184
|
+
nextChange();
|
|
185
|
+
} else {
|
|
186
|
+
this.stack.ongoingChange = false;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
// TODO: this queued changes are somewhat experimental
|
|
190
|
+
if (!this.stack.ongoingChange) {
|
|
191
|
+
this.stack.ongoingChange = true;
|
|
192
|
+
doChange();
|
|
193
|
+
} else {
|
|
194
|
+
this.stack.queuedScopeChanges.push(doChange);
|
|
195
|
+
}
|
|
196
|
+
return newContext;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Exits this scope (and all open subscopes=), if a scope class is given. all scopes from the stack are closed until after the scope with the given class
|
|
201
|
+
*/
|
|
202
|
+
exitScope(target?: Scope) {
|
|
203
|
+
const doChange = () => {
|
|
204
|
+
if (this.closed) throw new Error(`Scope already closed`);
|
|
205
|
+
// TODO: check if target is in stack?
|
|
206
|
+
this.stack.target = target || this.scope.constructor;
|
|
207
|
+
while (true) {
|
|
208
|
+
const ctx = this.stack.stack.pop();
|
|
209
|
+
if (!ctx) {
|
|
210
|
+
throw new Error('no scope in stack');
|
|
211
|
+
}
|
|
212
|
+
ctx!.exitThis();
|
|
213
|
+
if (ctx!.scope.constructor === this.stack.target) {
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
this.stack.target = undefined;
|
|
218
|
+
const nextChange = this.stack.queuedScopeChanges.shift();
|
|
219
|
+
if (nextChange) {
|
|
220
|
+
nextChange();
|
|
221
|
+
} else {
|
|
222
|
+
this.stack.ongoingChange = false;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
};
|
|
226
|
+
if (!this.stack.ongoingChange) {
|
|
227
|
+
this.stack.ongoingChange = true;
|
|
228
|
+
doChange();
|
|
229
|
+
} else {
|
|
230
|
+
this.stack.queuedScopeChanges.push(doChange);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
closeSubScopes() {
|
|
235
|
+
this.stack.target = this.scope.constructor;
|
|
236
|
+
while (!this.isActive) {
|
|
237
|
+
const ctx = this.stack.stack.pop();
|
|
238
|
+
if (!ctx) {
|
|
239
|
+
throw new Error('no scope in stack');
|
|
240
|
+
}
|
|
241
|
+
ctx.exitThis();
|
|
242
|
+
}
|
|
243
|
+
this.stack.target = undefined;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private logStack() {
|
|
247
|
+
if (SCOPE_CONTEXT.log) {
|
|
248
|
+
console.debug('%c' + this.stack.stack.map(c => c.scope.constructor.name).join(' -> '), 'color:yellow');
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
getStackClasses(): Scope[] {
|
|
253
|
+
return this.stack.stack.map(context => context.scope.constructor);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
addScopeSignal<T>(signal: Signal<T>, callback: SignalCallback<T>, params?: AddScopeSignalOptions) {
|
|
257
|
+
const signalBinding = new ScopeSignalBinding(signal.add(callback, params?.context), params);
|
|
258
|
+
this.signalBindings.push(signalBinding);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
disableKeepAliveSignals() {
|
|
262
|
+
this.muteKeepAliveSignals = true;
|
|
263
|
+
this.updateSignalBindings();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
enableKeepAliveSignals() {
|
|
267
|
+
this.muteKeepAliveSignals = false;
|
|
268
|
+
this.updateSignalBindings();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private updateSignalBindings(muteKeepAlive = false) {
|
|
272
|
+
for (const binding of this.signalBindings) {
|
|
273
|
+
if (this.isActive) {
|
|
274
|
+
binding.activate();
|
|
275
|
+
} else if (muteKeepAlive) {
|
|
276
|
+
binding.deactivate();
|
|
277
|
+
} else if (binding.keepAlive === true) {
|
|
278
|
+
binding.activate();
|
|
279
|
+
} else if (!!(binding.keepAlive)) {
|
|
280
|
+
// is array of scopes
|
|
281
|
+
// OPT: cache active context?
|
|
282
|
+
if (binding.keepAlive.indexOf(this.activeContext.scope.constructor) != -1) {
|
|
283
|
+
binding.activate();
|
|
284
|
+
} else {
|
|
285
|
+
binding.deactivate();
|
|
286
|
+
}
|
|
287
|
+
} else {
|
|
288
|
+
binding.deactivate();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
muteKeepAlive = muteKeepAlive || this.muteKeepAliveSignals;
|
|
292
|
+
this.parent?.updateSignalBindings(muteKeepAlive);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private detachSignalBindings() {
|
|
296
|
+
for (const signalBinding of this.signalBindings) {
|
|
297
|
+
signalBinding.detach();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private mapScopeSignals() {
|
|
302
|
+
const handlers = scopeSignalHandlers.get(this.scope.constructor);
|
|
303
|
+
if (!handlers) return;
|
|
304
|
+
|
|
305
|
+
for (const [field, id, options] of handlers) {
|
|
306
|
+
const signal = this.injector.getSignal(id);
|
|
307
|
+
const callback = (<any>this.scope) [field];
|
|
308
|
+
this.addScopeSignal(signal, callback, {...options, context: this.scope});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private mapServiceSignals(scopeService: Object) {
|
|
313
|
+
const handlers = signalHandlers.get(scopeService.constructor);
|
|
314
|
+
if (!handlers) return;
|
|
315
|
+
|
|
316
|
+
for (const [field, id] of handlers) {
|
|
317
|
+
const signal = this.injector.getSignal(id);
|
|
318
|
+
const signalBinding = signal.add((<any>scopeService) [field], scopeService);
|
|
319
|
+
this.serviceBindings.push(signalBinding);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private detachServiceBindings() {
|
|
324
|
+
for (const binding of this.serviceBindings) {
|
|
325
|
+
binding.detach();
|
|
326
|
+
}
|
|
327
|
+
this.serviceBindings = [];
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
private exitThis() {
|
|
331
|
+
|
|
332
|
+
// TODO: BUG: when there is a scope change within another scope change there is some confusion about the call order
|
|
333
|
+
if (!this.parent) {
|
|
334
|
+
throw new Error('can\'t exit root scope?!');
|
|
335
|
+
}
|
|
336
|
+
this.logStack();
|
|
337
|
+
this.detachSignalBindings();
|
|
338
|
+
this.detachServiceBindings();
|
|
339
|
+
this.scope.onDeactivate?.();
|
|
340
|
+
this.onDeactivate.dispatch(this);
|
|
341
|
+
|
|
342
|
+
this.scope.onExit?.();
|
|
343
|
+
this.onExit.dispatch(this);
|
|
344
|
+
this.parent.updateSignalBindings();
|
|
345
|
+
this.parent.scope.onSubReturn?.();
|
|
346
|
+
this.onSubReturn.dispatch(this);
|
|
347
|
+
this.parent.scope.onActivate?.();
|
|
348
|
+
this.parent.onActivate.dispatch(this);
|
|
349
|
+
this.closed = true;
|
|
350
|
+
|
|
351
|
+
this.onEnter.detachAll();
|
|
352
|
+
this.onExit.detachAll();
|
|
353
|
+
this.onSubReturn.detachAll();
|
|
354
|
+
this.onSubExit.detachAll();
|
|
355
|
+
this.onActivate.detachAll();
|
|
356
|
+
this.onDeactivate.detachAll();
|
|
357
|
+
|
|
358
|
+
}
|
|
359
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {System} from './types';
|
|
2
|
+
import {Entity, GameEngine, ID, putIfAbsent, Signal, SignalBinding, SignalCallback} from './index';
|
|
3
|
+
|
|
4
|
+
export const entitySignalHandlers = new Map<Object, [string, ID][]>();
|
|
5
|
+
export const OnEntitySignal = (id: ID) => (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
|
|
6
|
+
if (target instanceof Function) {
|
|
7
|
+
throw new Error('only allowed on non static methods');
|
|
8
|
+
} else {
|
|
9
|
+
const mappingList = putIfAbsent(entitySignalHandlers, target.constructor, (): [string, ID][] => []);
|
|
10
|
+
mappingList.push([propertyKey, id]);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export class SystemContext<T extends System = System> {
|
|
15
|
+
readonly system!: T;
|
|
16
|
+
private signalBindings: SignalBinding[] = [];
|
|
17
|
+
|
|
18
|
+
constructor(readonly entity: Entity) {
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// TODO: is this a bit hacky?
|
|
22
|
+
addSignalById<S>(signalId: string | symbol, callback: SignalCallback<S>, thisArg?: unknown) {
|
|
23
|
+
this.signalBindings.push(((this.entity as any).gameEngine as GameEngine).getSignal<S>(signalId).add(callback, thisArg));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
addSignal<S>(signal: Signal<S>, callback: SignalCallback<S>, thisArg?: unknown) {
|
|
27
|
+
this.signalBindings.push(signal.add(callback, thisArg));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
addSignalOnce<S>(signal: Signal<S>, callback: SignalCallback<S>, thisArg?: unknown) {
|
|
31
|
+
this.signalBindings.push(signal.addOnce(callback, thisArg));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
dispose() {
|
|
35
|
+
for (const binding of this.signalBindings) binding.detach();
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,70 +1,95 @@
|
|
|
1
|
-
import {GameEngine} from './gameEngine';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
onFixedUpdate?(time: number, deltaTime: number): void;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
onPostPhysicsUpdate?(time: number, deltaTime: number): void;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export interface
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
export interface
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
1
|
+
import {GameEngine} from './gameEngine';
|
|
2
|
+
import {Entity} from './entity';
|
|
3
|
+
|
|
4
|
+
export interface System {
|
|
5
|
+
[propName: string]: any;
|
|
6
|
+
onPrepare?(): Promise<void>;
|
|
7
|
+
onStart?(): void;
|
|
8
|
+
onEnd?(): void;
|
|
9
|
+
onPreFixedUpdate?(time: number, deltaTime: number): void;
|
|
10
|
+
onFixedUpdate?(time: number, deltaTime: number): void;
|
|
11
|
+
onPhysicsUpdate?(time: number, deltaTime: number): void;
|
|
12
|
+
onUpdate?(time: number, deltaTime: number, alpha: number): void;
|
|
13
|
+
onLateUpdate?(time: number, deltaTime: number, alpha: number): void;
|
|
14
|
+
onPrePhysicsUpdate?(time: number, deltaTime: number): void;
|
|
15
|
+
onPostPhysicsUpdate?(time: number, deltaTime: number): void;
|
|
16
|
+
onLateFixedUpdate?(time: number, deltaTime: number): void;
|
|
17
|
+
onRender?(time: number, deltaTime: number, alpha: number): void;
|
|
18
|
+
onAddEntity?(entity: Entity): void;
|
|
19
|
+
onRemoveEntity?(entity: Entity): void;
|
|
20
|
+
// new(...args: any[]): System;
|
|
21
|
+
// new(): GameSystem;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// export interface GameSystemClass {
|
|
25
|
+
// new(...args: any[]): GameSystem;
|
|
26
|
+
// }
|
|
27
|
+
|
|
28
|
+
export interface Service {
|
|
29
|
+
[propName: string]: any; // disable weak type detection
|
|
30
|
+
onPrepare?(): Promise<void>;
|
|
31
|
+
onStart?(): void;
|
|
32
|
+
onEnd?(): void;
|
|
33
|
+
onPreFixedUpdate?(time: number, deltaTime: number): void;
|
|
34
|
+
onPostPhysicsUpdate?(time: number, deltaTime: number): void;
|
|
35
|
+
onLateFixedUpdate?(time: number, deltaTime: number): void;
|
|
36
|
+
onFixedUpdate?(time: number, deltaTime: number): void;
|
|
37
|
+
onPhysicsUpdate?(time: number, deltaTime: number): void;
|
|
38
|
+
onPrePhysicsUpdate?(time: number, deltaTime: number): void;
|
|
39
|
+
onUpdate?(time: number, deltaTime: number, alpha: number): void;
|
|
40
|
+
onLateUpdate?(time: number, deltaTime: number, alpha: number): void;
|
|
41
|
+
onRender?(time: number, deltaTime: number, alpha: number): void;
|
|
42
|
+
onAddEntity?(entity: Entity): void;
|
|
43
|
+
onRemoveEntity?(entity: Entity): void;
|
|
44
|
+
// new(): GameSystem;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type GameEngineCallback = (gameEngine: GameEngine) => void;
|
|
48
|
+
|
|
49
|
+
export interface GameEnginePlugin {
|
|
50
|
+
// systems?: (SystemClass | GameSystemClass)[];
|
|
51
|
+
// services?: Service[];
|
|
52
|
+
onPrepare?: GameEngineCallback;
|
|
53
|
+
onStart?: GameEngineCallback;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface CommandClass {
|
|
57
|
+
name?: string;
|
|
58
|
+
|
|
59
|
+
new(...args: any[]): any;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type OnUpdateParams = {
|
|
63
|
+
time: number;
|
|
64
|
+
deltaTime: number;
|
|
65
|
+
alpha: number;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export type OnFixedUpdateParams = {
|
|
69
|
+
time: number;
|
|
70
|
+
deltaTime: number;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export type OnEarlyUpdateParams = { time: number, deltaTime: number };
|
|
74
|
+
|
|
75
|
+
export type AsyncAction<T = unknown> = (gameEngine: GameEngine) => Promise<T>;
|
|
76
|
+
// export type Action<T = unknown> = (gameEngine: GameEngine) => T;
|
|
77
|
+
export type Action<T = any> = (gameEngine: GameEngine) => T;
|
|
78
|
+
export type Class<T = any> = { new(...args: any[]): T; }
|
|
79
|
+
|
|
80
|
+
export const EngineSignals = {
|
|
81
|
+
OnStart: Symbol('OnStart'),
|
|
82
|
+
OnEnd: Symbol('OnEnd'),
|
|
83
|
+
OnUpdate: Symbol('OnUpdate'),
|
|
84
|
+
OnLateUpdate: Symbol('OnLateUpdate'),
|
|
85
|
+
OnFixedUpdate: Symbol('OnFixedUpdate'),
|
|
86
|
+
OnPreFixedUpdate: Symbol('OnPreFixedUpdate'),
|
|
87
|
+
OnPrePhysicsUpdate: Symbol('OnPrePhysicsUpdate'),
|
|
88
|
+
OnPhysicsUpdate: Symbol('OnPhysicsUpdate'),
|
|
89
|
+
OnPostPhysicsUpdate: Symbol('OnPostPhysicsUpdate'),
|
|
90
|
+
onLateFixedUpdate: Symbol('onLateFixedUpdate'),
|
|
91
|
+
OnRender: Symbol('OnRender'),
|
|
92
|
+
OnPrepare: Symbol('OnPrepare'),
|
|
93
|
+
OnAddEntity: Symbol('OnAddEntity'),
|
|
94
|
+
OnRemoveEntity: Symbol('OnRemoveEntity'),
|
|
95
|
+
};
|
package/src/utils/map2k.ts
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
export class Map2k<K, V> {
|
|
2
|
-
map = new Map<K, Map<K, V>>();
|
|
3
|
-
|
|
4
|
-
set(key1: K, key2: K, value: V) {
|
|
5
|
-
let first = this.map.get(key1);
|
|
6
|
-
if(!first) {
|
|
7
|
-
first = new Map<K, V>();
|
|
8
|
-
this.map.set(key1, first);
|
|
9
|
-
}
|
|
10
|
-
let second = this.map.get(key2);
|
|
11
|
-
if(!second) {
|
|
12
|
-
second = new Map<K, V>();
|
|
13
|
-
this.map.set(key2, second);
|
|
14
|
-
}
|
|
15
|
-
first.set(key2, value);
|
|
16
|
-
second.set(key1, value);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// putIfAbsent(key1: K, key2: K, cb: () => V): V {
|
|
20
|
-
// let first = this.map.get(key1);
|
|
21
|
-
// let value;
|
|
22
|
-
// if(!first) {
|
|
23
|
-
// first = new Map<K, V>();
|
|
24
|
-
// this.map.set(key1, first);
|
|
25
|
-
// value = cb();
|
|
26
|
-
// first.set(key2, value);
|
|
27
|
-
// }
|
|
28
|
-
// }
|
|
29
|
-
|
|
30
|
-
get(key1: K, key2: K): V | undefined {
|
|
31
|
-
const first = this.map.get(key1);
|
|
32
|
-
return first && first.get(key2);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
delete(key1:K, key2:K) {
|
|
36
|
-
let first = this.map.get(key1);
|
|
37
|
-
if (first) {
|
|
38
|
-
first.delete(key2);
|
|
39
|
-
if (first.size === 0) {
|
|
40
|
-
this.map.delete(key1)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
let second = this.map.get(key2);
|
|
44
|
-
if (second) {
|
|
45
|
-
second.delete(key1);
|
|
46
|
-
if (second.size === 0) {
|
|
47
|
-
this.map.delete(key2)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
}
|
|
1
|
+
export class Map2k<K, V> {
|
|
2
|
+
map = new Map<K, Map<K, V>>();
|
|
3
|
+
|
|
4
|
+
set(key1: K, key2: K, value: V) {
|
|
5
|
+
let first = this.map.get(key1);
|
|
6
|
+
if(!first) {
|
|
7
|
+
first = new Map<K, V>();
|
|
8
|
+
this.map.set(key1, first);
|
|
9
|
+
}
|
|
10
|
+
let second = this.map.get(key2);
|
|
11
|
+
if(!second) {
|
|
12
|
+
second = new Map<K, V>();
|
|
13
|
+
this.map.set(key2, second);
|
|
14
|
+
}
|
|
15
|
+
first.set(key2, value);
|
|
16
|
+
second.set(key1, value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// putIfAbsent(key1: K, key2: K, cb: () => V): V {
|
|
20
|
+
// let first = this.map.get(key1);
|
|
21
|
+
// let value;
|
|
22
|
+
// if(!first) {
|
|
23
|
+
// first = new Map<K, V>();
|
|
24
|
+
// this.map.set(key1, first);
|
|
25
|
+
// value = cb();
|
|
26
|
+
// first.set(key2, value);
|
|
27
|
+
// }
|
|
28
|
+
// }
|
|
29
|
+
|
|
30
|
+
get(key1: K, key2: K): V | undefined {
|
|
31
|
+
const first = this.map.get(key1);
|
|
32
|
+
return first && first.get(key2);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
delete(key1:K, key2:K) {
|
|
36
|
+
let first = this.map.get(key1);
|
|
37
|
+
if (first) {
|
|
38
|
+
first.delete(key2);
|
|
39
|
+
if (first.size === 0) {
|
|
40
|
+
this.map.delete(key1)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
let second = this.map.get(key2);
|
|
44
|
+
if (second) {
|
|
45
|
+
second.delete(key1);
|
|
46
|
+
if (second.size === 0) {
|
|
47
|
+
this.map.delete(key2)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
}
|