@byloth/core 2.0.0-rc.6 → 2.0.0-rc.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byloth/core",
3
- "version": "2.0.0-rc.6",
3
+ "version": "2.0.0-rc.7",
4
4
  "description": "An unopinionated collection of useful functions and classes that I use widely in all my projects. 🔧",
5
5
  "keywords": [
6
6
  "Core",
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export const VERSION = "2.0.0-rc.6";
1
+ export const VERSION = "2.0.0-rc.7";
2
2
 
3
3
  export type { Constructor, Interval, Timeout } from "./core/types.js";
4
4
 
@@ -2,37 +2,44 @@ import { ReferenceException } from "./exceptions/index.js";
2
2
 
3
3
  export type Subscriber<A extends unknown[] = [], R = void> = (...args: A) => R;
4
4
 
5
- export default class Publisher<A extends unknown[] = [], R = void>
5
+ // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
6
+ export default class Publisher<T extends { [K in keyof T]: [unknown[], unknown] } = Record<string, [[], void]>>
6
7
  {
7
- protected _subscribers: Subscriber<A, R>[];
8
+ protected _subscribers: Map<keyof T, Subscriber<unknown[], unknown>[]>;
8
9
 
9
10
  public constructor()
10
11
  {
11
- this._subscribers = [];
12
+ this._subscribers = new Map();
12
13
  }
13
14
 
14
- public subscribe(subscriber: Subscriber<A, R>): () => void
15
+ public subscribe<K extends keyof T, A extends T[K][0], R extends T[K][1]>(event: K, subscriber: Subscriber<A, R>)
16
+ : () => void
15
17
  {
16
- this._subscribers.push(subscriber);
18
+ if (!(this._subscribers.has(event))) { this._subscribers.set(event, []); }
19
+
20
+ const subscribers = this._subscribers.get(event)!;
21
+ subscribers.push(subscriber as Subscriber<unknown[], unknown>);
17
22
 
18
23
  return () =>
19
24
  {
20
- const index = this._subscribers.indexOf(subscriber);
25
+ const index = subscribers.indexOf(subscriber as Subscriber<unknown[], unknown>);
21
26
  if (index < 0)
22
27
  {
23
28
  throw new ReferenceException("Unable to unsubscribe the required subscriber. " +
24
29
  "The subscription was already unsubscribed.");
25
30
  }
26
31
 
27
- this._subscribers.splice(index, 1);
32
+ subscribers.splice(index, 1);
28
33
  };
29
34
  }
30
35
 
31
- public publish(...args: A): R[]
36
+ public publish<K extends keyof T, A extends T[K][0], R extends T[K][1]>(event: K, ...args: A): R[]
32
37
  {
33
- return this._subscribers
34
- .slice()
35
- .map((subscriber) => subscriber(...args));
38
+ const subscribers = this._subscribers.get(event);
39
+ if (!(subscribers)) { return []; }
40
+
41
+ return subscribers.slice()
42
+ .map((subscriber) => subscriber(...args)) as R[];
36
43
  }
37
44
 
38
45
  public readonly [Symbol.toStringTag]: string = "Publisher";
@@ -1,22 +1,27 @@
1
1
  import { TimeUnit } from "../../utils/date.js";
2
-
3
2
  import { RangeException, RuntimeException } from "../exceptions/index.js";
3
+
4
4
  import GameLoop from "../game-loop.js";
5
5
  import Publisher from "../publisher.js";
6
6
 
7
+ interface ClockEvents
8
+ {
9
+ /* eslint-disable @typescript-eslint/no-invalid-void-type */
10
+
11
+ start: [[], void];
12
+ stop: [[], void];
13
+ tick: [[number], void];
14
+ }
15
+
7
16
  export default class Clock extends GameLoop
8
17
  {
9
- protected _starter: Publisher;
10
- protected _stopper: Publisher;
11
- protected _ticker: Publisher<[number]>;
18
+ protected _publisher: Publisher<ClockEvents>;
12
19
 
13
20
  public constructor(msIfNotBrowser: number = TimeUnit.Second)
14
21
  {
15
- super((elapsedTime) => this._ticker.publish(elapsedTime), msIfNotBrowser);
22
+ super((elapsedTime) => this._publisher.publish("tick", elapsedTime), msIfNotBrowser);
16
23
 
17
- this._starter = new Publisher();
18
- this._stopper = new Publisher();
19
- this._ticker = new Publisher();
24
+ this._publisher = new Publisher();
20
25
  }
21
26
 
22
27
  public start(elapsedTime = 0): void
@@ -25,7 +30,7 @@ export default class Clock extends GameLoop
25
30
 
26
31
  super.start(elapsedTime);
27
32
 
28
- this._starter.publish();
33
+ this._publisher.publish("start");
29
34
  }
30
35
 
31
36
  public stop(): void
@@ -34,26 +39,26 @@ export default class Clock extends GameLoop
34
39
 
35
40
  super.stop();
36
41
 
37
- this._stopper.publish();
42
+ this._publisher.publish("stop");
38
43
  }
39
44
 
40
45
  public onStart(callback: () => void): () => void
41
46
  {
42
- return this._starter.subscribe(callback);
47
+ return this._publisher.subscribe("start", callback);
43
48
  }
44
49
  public onStop(callback: () => void): () => void
45
50
  {
46
- return this._stopper.subscribe(callback);
51
+ return this._publisher.subscribe("stop", callback);
47
52
  }
48
53
 
49
54
  public onTick(callback: (elapsedTime: number) => void, tickStep = 0): () => void
50
55
  {
51
56
  if (tickStep < 0) { throw new RangeException("The tick step must be a non-negative number."); }
52
- if (tickStep === 0) { return this._ticker.subscribe(callback); }
57
+ if (tickStep === 0) { return this._publisher.subscribe("tick", callback); }
53
58
 
54
59
  let lastTick = 0;
55
60
 
56
- return this._ticker.subscribe((elapsedTime: number) =>
61
+ return this._publisher.subscribe("tick", (elapsedTime: number) =>
57
62
  {
58
63
  if ((elapsedTime - lastTick) < tickStep) { return; }
59
64
 
@@ -6,14 +6,21 @@ import { DeferredPromise, SmartPromise } from "../promises/index.js";
6
6
  import GameLoop from "../game-loop.js";
7
7
  import Publisher from "../publisher.js";
8
8
 
9
+ interface CountdownEvents
10
+ {
11
+ /* eslint-disable @typescript-eslint/no-invalid-void-type */
12
+
13
+ start: [[], void];
14
+ stop: [[unknown], void];
15
+ tick: [[number], void];
16
+ expire: [[], void];
17
+ }
18
+
9
19
  export default class Countdown extends GameLoop
10
20
  {
11
21
  protected _deferrer?: DeferredPromise<void>;
12
22
 
13
- protected _expirer: Publisher;
14
- protected _starter: Publisher;
15
- protected _stopper: Publisher<[unknown]>;
16
- protected _ticker: Publisher<[number]>;
23
+ protected _publisher: Publisher<CountdownEvents>;
17
24
 
18
25
  protected _duration: number;
19
26
  public get duration(): number
@@ -31,23 +38,19 @@ export default class Countdown extends GameLoop
31
38
  const callback = () =>
32
39
  {
33
40
  const remainingTime = this.remainingTime;
34
- this._ticker.publish(remainingTime);
41
+ this._publisher.publish("tick", remainingTime);
35
42
 
36
43
  if (remainingTime <= 0)
37
44
  {
38
45
  this._deferrerStop();
39
46
 
40
- this._expirer.publish();
47
+ this._publisher.publish("expire");
41
48
  }
42
49
  };
43
50
 
44
51
  super(callback, msIfNotBrowser);
45
52
 
46
- this._expirer = new Publisher();
47
- this._starter = new Publisher();
48
- this._stopper = new Publisher();
49
- this._ticker = new Publisher();
50
-
53
+ this._publisher = new Publisher();
51
54
  this._duration = duration;
52
55
  }
53
56
 
@@ -72,7 +75,7 @@ export default class Countdown extends GameLoop
72
75
  this._deferrer = new DeferredPromise();
73
76
  super.start(this.duration - remainingTime);
74
77
 
75
- this._starter.publish();
78
+ this._publisher.publish("start");
76
79
 
77
80
  return this._deferrer;
78
81
  }
@@ -80,31 +83,31 @@ export default class Countdown extends GameLoop
80
83
  {
81
84
  this._deferrerStop(reason);
82
85
 
83
- this._stopper.publish(reason);
86
+ this._publisher.publish("stop", reason);
84
87
  }
85
88
 
86
89
  public onExpire(callback: () => void): () => void
87
90
  {
88
- return this._expirer.subscribe(callback);
91
+ return this._publisher.subscribe("expire", callback);
89
92
  }
90
93
 
91
94
  public onStart(callback: () => void): () => void
92
95
  {
93
- return this._starter.subscribe(callback);
96
+ return this._publisher.subscribe("start", callback);
94
97
  }
95
98
  public onStop(callback: (reason?: unknown) => void): () => void
96
99
  {
97
- return this._stopper.subscribe(callback);
100
+ return this._publisher.subscribe("stop", callback);
98
101
  }
99
102
 
100
103
  public onTick(callback: (remainingTime: number) => void, tickStep = 0): () => void
101
104
  {
102
105
  if (tickStep < 0) { throw new RangeException("The tick step must be a non-negative number."); }
103
- if (tickStep === 0) { return this._ticker.subscribe(callback); }
106
+ if (tickStep === 0) { return this._publisher.subscribe("tick", callback); }
104
107
 
105
108
  let lastTick = 0;
106
109
 
107
- return this._ticker.subscribe((remainingTime: number) =>
110
+ return this._publisher.subscribe("tick", (remainingTime: number) =>
108
111
  {
109
112
  if ((lastTick - remainingTime) < tickStep) { return; }
110
113