@antha/asset 0.3.0 → 0.4.1
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/README.md +29 -5
- package/dist/antha-asset.mod.d.ts +2 -20
- package/dist/antha-asset.mod.js +13 -39
- package/dist/asset-loader.d.ts +84 -40
- package/dist/asset-loader.js +175 -41
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -14,7 +14,12 @@ npm i @antha/asset
|
|
|
14
14
|
|
|
15
15
|
```TypeScript
|
|
16
16
|
import {AnthaEngine, defineAnthaMod} from '@antha/engine';
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
type AnthaAssetModState,
|
|
19
|
+
type AssetLoader,
|
|
20
|
+
createAnthaAssetMod,
|
|
21
|
+
defineAsset,
|
|
22
|
+
} from '@antha/asset';
|
|
18
23
|
|
|
19
24
|
type GameState = AnthaAssetModState & {
|
|
20
25
|
hasLoadedTitle: boolean;
|
|
@@ -31,6 +36,25 @@ const titleAsset = defineAsset({
|
|
|
31
36
|
};
|
|
32
37
|
},
|
|
33
38
|
});
|
|
39
|
+
|
|
40
|
+
async function loadTitleAsset({
|
|
41
|
+
assetLoader,
|
|
42
|
+
}: Readonly<{
|
|
43
|
+
assetLoader: AssetLoader;
|
|
44
|
+
}>) {
|
|
45
|
+
const loadSession = assetLoader.createLoadSession();
|
|
46
|
+
|
|
47
|
+
await assetLoader.bulkLoadAssets(
|
|
48
|
+
[
|
|
49
|
+
titleAsset,
|
|
50
|
+
],
|
|
51
|
+
{
|
|
52
|
+
loadSession,
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
loadSession.complete();
|
|
56
|
+
}
|
|
57
|
+
|
|
34
58
|
const engine = new AnthaEngine<GameState>({
|
|
35
59
|
initState: {
|
|
36
60
|
hasLoadedTitle: false,
|
|
@@ -39,12 +63,12 @@ const engine = new AnthaEngine<GameState>({
|
|
|
39
63
|
createAnthaAssetMod(),
|
|
40
64
|
defineAnthaMod<GameState>({
|
|
41
65
|
modName: 'game-logic',
|
|
42
|
-
|
|
66
|
+
execute({state}) {
|
|
43
67
|
if (state.assetLoader && !state.hasLoadedTitle) {
|
|
44
68
|
state.hasLoadedTitle = true;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
69
|
+
void loadTitleAsset({
|
|
70
|
+
assetLoader: state.assetLoader,
|
|
71
|
+
});
|
|
48
72
|
}
|
|
49
73
|
},
|
|
50
74
|
}),
|
|
@@ -1,21 +1,5 @@
|
|
|
1
1
|
import { type PartialWithUndefined } from '@augment-vir/common';
|
|
2
2
|
import { AssetLoader } from './asset-loader.js';
|
|
3
|
-
/**
|
|
4
|
-
* Engine state for the Antha asset mod loading screen.
|
|
5
|
-
*
|
|
6
|
-
* @category Internal
|
|
7
|
-
*/
|
|
8
|
-
export type AnthaAssetModLoadingScreenState = {
|
|
9
|
-
/** The total number to load. Once `current` reaches this, loading is considered complete. */
|
|
10
|
-
total: number;
|
|
11
|
-
/**
|
|
12
|
-
* The current number of loaded assets. Once this reaches `total`, the loading is considered
|
|
13
|
-
* complete.
|
|
14
|
-
*/
|
|
15
|
-
current: number;
|
|
16
|
-
currentResourceName?: string | undefined;
|
|
17
|
-
completedAt: DOMHighResTimeStamp | undefined;
|
|
18
|
-
};
|
|
19
3
|
/**
|
|
20
4
|
* State for {@link AnthaAssetMod}.
|
|
21
5
|
*
|
|
@@ -23,8 +7,6 @@ export type AnthaAssetModLoadingScreenState = {
|
|
|
23
7
|
*/
|
|
24
8
|
export type AnthaAssetModState = {
|
|
25
9
|
assetLoader: AssetLoader;
|
|
26
|
-
isShowingLoadingScreen: boolean;
|
|
27
|
-
loadingScreenState: AnthaAssetModLoadingScreenState | undefined;
|
|
28
10
|
};
|
|
29
11
|
/**
|
|
30
12
|
* Configuration options for {@link createAnthaAssetMod}.
|
|
@@ -33,8 +15,8 @@ export type AnthaAssetModState = {
|
|
|
33
15
|
*/
|
|
34
16
|
export type AnthaAssetModOptions = PartialWithUndefined<{
|
|
35
17
|
/**
|
|
36
|
-
* If set to `true`, the default loading screen is not rendered.
|
|
37
|
-
*
|
|
18
|
+
* If set to `true`, the default loading screen is not rendered. Loading-session state remains
|
|
19
|
+
* available for a custom loading screen.
|
|
38
20
|
*
|
|
39
21
|
* @default false
|
|
40
22
|
*/
|
package/dist/antha-asset.mod.js
CHANGED
|
@@ -2,7 +2,7 @@ import { defineAnthaMod } from '@antha/engine';
|
|
|
2
2
|
import { addSuffix } from '@augment-vir/common';
|
|
3
3
|
import { css, defineElement, html } from 'element-vir';
|
|
4
4
|
import { setCssVarValue } from 'lit-css-vars';
|
|
5
|
-
import { AssetLoader
|
|
5
|
+
import { AssetLoader } from './asset-loader.js';
|
|
6
6
|
/**
|
|
7
7
|
* Duration in milliseconds for the loading screen fade-out animation.
|
|
8
8
|
*
|
|
@@ -135,62 +135,36 @@ export function createAnthaAssetMod(options = {}) {
|
|
|
135
135
|
modName: anthaAssetModName,
|
|
136
136
|
async cleanup({ state }) {
|
|
137
137
|
await state.assetLoader?.destroy();
|
|
138
|
-
state.loadingScreenState = undefined;
|
|
139
|
-
state.isShowingLoadingScreen = false;
|
|
140
138
|
},
|
|
141
139
|
execute({ state, engine }) {
|
|
142
140
|
if (!state.assetLoader) {
|
|
143
141
|
state.assetLoader = new AssetLoader({
|
|
144
142
|
logger: engine.log,
|
|
145
143
|
});
|
|
146
|
-
if (!options.hideLoadingScreen) {
|
|
147
|
-
state.assetLoader.listen(AssetLoaderProgressUpdateEvent, (event) => {
|
|
148
|
-
if (event.detail.complete) {
|
|
149
|
-
state.loadingScreenState = {
|
|
150
|
-
current: 1,
|
|
151
|
-
total: 1,
|
|
152
|
-
currentResourceName: event.detail.currentResourceName ||
|
|
153
|
-
state.loadingScreenState?.currentResourceName,
|
|
154
|
-
completedAt: engine.totalMs,
|
|
155
|
-
};
|
|
156
|
-
state.isShowingLoadingScreen = false;
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
state.isShowingLoadingScreen = true;
|
|
160
|
-
state.loadingScreenState = {
|
|
161
|
-
current: event.detail.current,
|
|
162
|
-
total: event.detail.total,
|
|
163
|
-
currentResourceName: event.detail.currentResourceName,
|
|
164
|
-
completedAt: undefined,
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
144
|
}
|
|
145
|
+
state.assetLoader.advanceLoadState({
|
|
146
|
+
currentTick: engine.currentTick,
|
|
147
|
+
totalMs: engine.totalMs,
|
|
148
|
+
});
|
|
170
149
|
if (options.hideLoadingScreen) {
|
|
171
150
|
return;
|
|
172
151
|
}
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const progressPercent = state.loadingScreenState.total > 0
|
|
179
|
-
? (state.loadingScreenState.current / state.loadingScreenState.total) * 100
|
|
180
|
-
: 0;
|
|
152
|
+
const loadState = state.assetLoader.loadState;
|
|
153
|
+
if (loadState &&
|
|
154
|
+
(loadState.completedAt == undefined ||
|
|
155
|
+
engine.totalMs <= loadState.completedAt + configuredLoadingScreenFadeMs)) {
|
|
156
|
+
const progressPercent = loadState.total > 0 ? (loadState.current / loadState.total) * 100 : 0;
|
|
181
157
|
return html `
|
|
182
158
|
<${AnthaAssetLoadingScreen.assign({
|
|
183
159
|
progressPercent,
|
|
184
160
|
dotCount: Math.floor(engine.totalMs / 500) % 4,
|
|
185
|
-
completed:
|
|
186
|
-
currentResourceName:
|
|
161
|
+
completed: loadState.completedAt != undefined,
|
|
162
|
+
currentResourceName: loadState.currentResourceName,
|
|
187
163
|
loadingScreenFadeMs: configuredLoadingScreenFadeMs,
|
|
188
164
|
})}></${AnthaAssetLoadingScreen}>
|
|
189
165
|
`;
|
|
190
166
|
}
|
|
191
|
-
|
|
192
|
-
return undefined;
|
|
193
|
-
}
|
|
167
|
+
return undefined;
|
|
194
168
|
},
|
|
195
169
|
});
|
|
196
170
|
}
|
package/dist/asset-loader.d.ts
CHANGED
|
@@ -79,12 +79,8 @@ export type AssetBulkLoaderLoadOptions = PartialWithUndefined<{
|
|
|
79
79
|
* @default false
|
|
80
80
|
*/
|
|
81
81
|
doNotUnload: boolean;
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
*
|
|
85
|
-
* @default false
|
|
86
|
-
*/
|
|
87
|
-
hideLoadingScreen: boolean;
|
|
82
|
+
/** Receives this bulk load's progress. */
|
|
83
|
+
loadSession: AssetLoadSession;
|
|
88
84
|
}>;
|
|
89
85
|
/**
|
|
90
86
|
* Options for {@link AssetLoader}.
|
|
@@ -98,75 +94,123 @@ export type AssetLoaderOptions = PartialWithUndefined<{
|
|
|
98
94
|
*/
|
|
99
95
|
logger: AnthaLogger;
|
|
100
96
|
}>;
|
|
101
|
-
|
|
97
|
+
/** Progress tracked by an {@link AssetLoadSession}. */
|
|
98
|
+
export type AssetLoadProgress = {
|
|
99
|
+
current: number;
|
|
100
|
+
total: number;
|
|
101
|
+
currentResourceName?: string | undefined;
|
|
102
|
+
};
|
|
103
|
+
/** State of the active asset load. */
|
|
104
|
+
export type AssetLoadState = AssetLoadProgress & {
|
|
105
|
+
completedAt: DOMHighResTimeStamp | undefined;
|
|
106
|
+
isLoading: boolean;
|
|
107
|
+
};
|
|
108
|
+
declare const AssetLoadSessionUpdateEvent_base: (new (eventInitDict: {
|
|
102
109
|
bubbles?: boolean;
|
|
103
110
|
cancelable?: boolean;
|
|
104
111
|
composed?: boolean;
|
|
105
|
-
detail: {
|
|
106
|
-
|
|
107
|
-
total: number;
|
|
108
|
-
currentResourceName?: string | undefined;
|
|
109
|
-
/**
|
|
110
|
-
* Always check this complete field first, as any misconfigured assets ma not correctly
|
|
111
|
-
* increment `total` but complete will always reliably mark the end of loading.
|
|
112
|
-
*/
|
|
112
|
+
detail: AssetLoadProgress & {
|
|
113
|
+
/** Indicates that the caller explicitly requested load completion. */
|
|
113
114
|
complete: boolean;
|
|
114
115
|
};
|
|
115
|
-
}) => import("typed-event-target").TypedCustomEvent<{
|
|
116
|
-
|
|
117
|
-
total: number;
|
|
118
|
-
currentResourceName?: string | undefined;
|
|
119
|
-
/**
|
|
120
|
-
* Always check this complete field first, as any misconfigured assets ma not correctly
|
|
121
|
-
* increment `total` but complete will always reliably mark the end of loading.
|
|
122
|
-
*/
|
|
116
|
+
}) => import("typed-event-target").TypedCustomEvent<AssetLoadProgress & {
|
|
117
|
+
/** Indicates that the caller explicitly requested load completion. */
|
|
123
118
|
complete: boolean;
|
|
124
|
-
}, "antha-asset-
|
|
119
|
+
}, "antha-asset-load-session-update-event">) & Pick<{
|
|
125
120
|
new (type: string, eventInitDict?: EventInit): Event;
|
|
126
121
|
prototype: Event;
|
|
127
122
|
readonly NONE: 0;
|
|
128
123
|
readonly CAPTURING_PHASE: 1;
|
|
129
124
|
readonly AT_TARGET: 2;
|
|
130
125
|
readonly BUBBLING_PHASE: 3;
|
|
131
|
-
}, "prototype" | "NONE" | "CAPTURING_PHASE" | "AT_TARGET" | "BUBBLING_PHASE"> & Pick<import("typed-event-target").TypedCustomEvent<{
|
|
132
|
-
|
|
133
|
-
total: number;
|
|
134
|
-
currentResourceName?: string | undefined;
|
|
135
|
-
/**
|
|
136
|
-
* Always check this complete field first, as any misconfigured assets ma not correctly
|
|
137
|
-
* increment `total` but complete will always reliably mark the end of loading.
|
|
138
|
-
*/
|
|
126
|
+
}, "prototype" | "NONE" | "CAPTURING_PHASE" | "AT_TARGET" | "BUBBLING_PHASE"> & Pick<import("typed-event-target").TypedCustomEvent<AssetLoadProgress & {
|
|
127
|
+
/** Indicates that the caller explicitly requested load completion. */
|
|
139
128
|
complete: boolean;
|
|
140
|
-
}, "antha-asset-
|
|
129
|
+
}, "antha-asset-load-session-update-event">, "type">;
|
|
130
|
+
/** Event dispatched when an {@link AssetLoadSession} progresses or completes. */
|
|
131
|
+
export declare class AssetLoadSessionUpdateEvent extends AssetLoadSessionUpdateEvent_base {
|
|
132
|
+
}
|
|
133
|
+
/** Manages the progress and explicit completion of an asset load. */
|
|
134
|
+
export declare class AssetLoadSession extends ListenTarget<AssetLoadSessionUpdateEvent> {
|
|
135
|
+
protected currentProgress: AssetLoadProgress;
|
|
136
|
+
protected isComplete: boolean;
|
|
137
|
+
/** Reports a load-progress update without completing the session. */
|
|
138
|
+
reportProgress(progress: Readonly<AssetLoadProgress>): void;
|
|
139
|
+
/** Adds progress to the active resource. */
|
|
140
|
+
incrementProgress({ amount, currentResourceName, }: Readonly<{
|
|
141
|
+
amount?: number | undefined;
|
|
142
|
+
currentResourceName: string;
|
|
143
|
+
}>): void;
|
|
144
|
+
/** Marks this asset load as complete. */
|
|
145
|
+
complete(): void;
|
|
146
|
+
}
|
|
141
147
|
/**
|
|
142
|
-
*
|
|
143
|
-
* loading screen progression.
|
|
148
|
+
* Maintains the active asset-load session and its state.
|
|
144
149
|
*
|
|
145
150
|
* @category Internal
|
|
146
151
|
*/
|
|
147
|
-
export declare class
|
|
152
|
+
export declare class AssetLoadSessionController {
|
|
153
|
+
protected currentLoadSessionInternal: AssetLoadSession;
|
|
154
|
+
protected loadStateInternal: AssetLoadState | undefined;
|
|
155
|
+
protected completionRequestedAtTick: number | undefined;
|
|
156
|
+
protected latestEngineTick: number;
|
|
157
|
+
/** Removes the listener for the active load session. */
|
|
158
|
+
protected removeLoadSessionListener: (() => boolean) | undefined;
|
|
159
|
+
constructor();
|
|
160
|
+
/** The active asset-load session. */
|
|
161
|
+
get currentLoadSession(): AssetLoadSession;
|
|
162
|
+
/** The active asset-load state. */
|
|
163
|
+
get loadState(): AssetLoadState | undefined;
|
|
164
|
+
/** Creates and activates a new asset-load session. */
|
|
165
|
+
createLoadSession(): AssetLoadSession;
|
|
166
|
+
/** Advances asset-load completion after an engine render. */
|
|
167
|
+
advance({ currentTick, totalMs, }: Readonly<{
|
|
168
|
+
currentTick: number;
|
|
169
|
+
totalMs: DOMHighResTimeStamp;
|
|
170
|
+
}>): void;
|
|
171
|
+
/** Stops tracking the active load session. */
|
|
172
|
+
destroy(): void;
|
|
173
|
+
/** Tracks updates from the active load session. */
|
|
174
|
+
protected listenToLoadSession(loadSession: AssetLoadSession): void;
|
|
148
175
|
}
|
|
149
176
|
/**
|
|
150
177
|
* Manages loading, caching, and cleanup of game assets with progress tracking.
|
|
151
178
|
*
|
|
152
179
|
* @category Asset
|
|
153
180
|
*/
|
|
154
|
-
export declare class AssetLoader
|
|
181
|
+
export declare class AssetLoader {
|
|
155
182
|
constructor(options?: Readonly<AssetLoaderOptions>);
|
|
156
183
|
/** Logs data. This will use the user's provided logger or default to browser logs. */
|
|
157
184
|
protected readonly log: AnthaLogger;
|
|
158
185
|
protected readonly assetCache: Map<Readonly<Asset<any>>, Promise<AssetLoaderResult<any>>>;
|
|
186
|
+
protected readonly loadSessionController: AssetLoadSessionController;
|
|
187
|
+
/** The active asset-load session. */
|
|
188
|
+
get currentLoadSession(): AssetLoadSession;
|
|
189
|
+
/** The active asset-load state. */
|
|
190
|
+
get loadState(): AssetLoadState | undefined;
|
|
191
|
+
/** Creates and activates a new asset-load session. */
|
|
192
|
+
createLoadSession(): AssetLoadSession;
|
|
193
|
+
/** Advances asset-load completion after an engine render. */
|
|
194
|
+
advanceLoadState({ currentTick, totalMs, }: Readonly<{
|
|
195
|
+
currentTick: number;
|
|
196
|
+
totalMs: DOMHighResTimeStamp;
|
|
197
|
+
}>): void;
|
|
159
198
|
/** Loads a single asset, returning its cached value if already loaded. */
|
|
160
|
-
loadIndividualAsset<ThisAsset extends Asset>({ asset, incrementProgressCallback, }: Readonly<{
|
|
199
|
+
loadIndividualAsset<ThisAsset extends Asset>({ asset, incrementProgressCallback, loadSession, }: Readonly<{
|
|
161
200
|
asset: Readonly<ThisAsset>;
|
|
162
201
|
incrementProgressCallback?: AssetIncrementProgressCallback | undefined;
|
|
202
|
+
loadSession?: AssetLoadSession | undefined;
|
|
163
203
|
}>): Promise<AssetValue<ThisAsset>>;
|
|
164
204
|
/** Runs cleanup callbacks for the given assets and removes them from the cache. */
|
|
165
205
|
unloadAssets(assets: ReadonlyArray<Asset>): Promise<void>;
|
|
206
|
+
/** Cleans up cached assets and stops tracking the active load session. */
|
|
166
207
|
destroy(): Promise<void>;
|
|
167
208
|
/** Loads multiple assets. */
|
|
168
209
|
bulkLoadAssets(assets: ReadonlyArray<Readonly<Asset>>, options?: Readonly<AssetBulkLoaderLoadOptions>): Promise<ReadonlyArray<unknown>>;
|
|
169
|
-
/**
|
|
170
|
-
protected
|
|
210
|
+
/** Sends load progress to the provided session. */
|
|
211
|
+
protected reportProgress({ loadSession, progress, }: Readonly<{
|
|
212
|
+
loadSession: AssetLoadSession | undefined;
|
|
213
|
+
progress: AssetLoadProgress;
|
|
214
|
+
}>): void;
|
|
171
215
|
}
|
|
172
216
|
export {};
|
package/dist/asset-loader.js
CHANGED
|
@@ -9,29 +9,161 @@ import { defineTypedCustomEvent, ListenTarget } from 'typed-event-target';
|
|
|
9
9
|
export function defineAsset(asset) {
|
|
10
10
|
return asset;
|
|
11
11
|
}
|
|
12
|
+
/** Event dispatched when an {@link AssetLoadSession} progresses or completes. */
|
|
13
|
+
export class AssetLoadSessionUpdateEvent extends defineTypedCustomEvent()('antha-asset-load-session-update-event') {
|
|
14
|
+
}
|
|
15
|
+
/** Manages the progress and explicit completion of an asset load. */
|
|
16
|
+
export class AssetLoadSession extends ListenTarget {
|
|
17
|
+
currentProgress = {
|
|
18
|
+
current: 0,
|
|
19
|
+
currentResourceName: undefined,
|
|
20
|
+
total: 0,
|
|
21
|
+
};
|
|
22
|
+
isComplete = false;
|
|
23
|
+
/** Reports a load-progress update without completing the session. */
|
|
24
|
+
reportProgress(progress) {
|
|
25
|
+
this.currentProgress = progress;
|
|
26
|
+
this.isComplete = false;
|
|
27
|
+
this.dispatch(new AssetLoadSessionUpdateEvent({
|
|
28
|
+
detail: {
|
|
29
|
+
...progress,
|
|
30
|
+
complete: false,
|
|
31
|
+
},
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
/** Adds progress to the active resource. */
|
|
35
|
+
incrementProgress({ amount, currentResourceName, }) {
|
|
36
|
+
this.reportProgress({
|
|
37
|
+
...this.currentProgress,
|
|
38
|
+
current: this.currentProgress.current + (amount ?? 1),
|
|
39
|
+
currentResourceName,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** Marks this asset load as complete. */
|
|
43
|
+
complete() {
|
|
44
|
+
if (this.isComplete) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
this.isComplete = true;
|
|
48
|
+
this.dispatch(new AssetLoadSessionUpdateEvent({
|
|
49
|
+
detail: {
|
|
50
|
+
...this.currentProgress,
|
|
51
|
+
complete: true,
|
|
52
|
+
},
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
12
56
|
/**
|
|
13
|
-
*
|
|
14
|
-
* loading screen progression.
|
|
57
|
+
* Maintains the active asset-load session and its state.
|
|
15
58
|
*
|
|
16
59
|
* @category Internal
|
|
17
60
|
*/
|
|
18
|
-
export class
|
|
61
|
+
export class AssetLoadSessionController {
|
|
62
|
+
currentLoadSessionInternal;
|
|
63
|
+
loadStateInternal;
|
|
64
|
+
completionRequestedAtTick;
|
|
65
|
+
latestEngineTick = 0;
|
|
66
|
+
/** Removes the listener for the active load session. */
|
|
67
|
+
removeLoadSessionListener;
|
|
68
|
+
constructor() {
|
|
69
|
+
const initialLoadSession = new AssetLoadSession();
|
|
70
|
+
this.currentLoadSessionInternal = initialLoadSession;
|
|
71
|
+
this.listenToLoadSession(initialLoadSession);
|
|
72
|
+
}
|
|
73
|
+
/** The active asset-load session. */
|
|
74
|
+
get currentLoadSession() {
|
|
75
|
+
return this.currentLoadSessionInternal;
|
|
76
|
+
}
|
|
77
|
+
/** The active asset-load state. */
|
|
78
|
+
get loadState() {
|
|
79
|
+
return this.loadStateInternal;
|
|
80
|
+
}
|
|
81
|
+
/** Creates and activates a new asset-load session. */
|
|
82
|
+
createLoadSession() {
|
|
83
|
+
const loadSession = new AssetLoadSession();
|
|
84
|
+
this.removeLoadSessionListener?.();
|
|
85
|
+
this.currentLoadSessionInternal = loadSession;
|
|
86
|
+
this.listenToLoadSession(loadSession);
|
|
87
|
+
loadSession.reportProgress({
|
|
88
|
+
current: 0,
|
|
89
|
+
total: 0,
|
|
90
|
+
});
|
|
91
|
+
return loadSession;
|
|
92
|
+
}
|
|
93
|
+
/** Advances asset-load completion after an engine render. */
|
|
94
|
+
advance({ currentTick, totalMs, }) {
|
|
95
|
+
this.latestEngineTick = currentTick + 1;
|
|
96
|
+
if (this.completionRequestedAtTick != undefined &&
|
|
97
|
+
this.completionRequestedAtTick < currentTick &&
|
|
98
|
+
this.loadStateInternal) {
|
|
99
|
+
this.completionRequestedAtTick = undefined;
|
|
100
|
+
this.loadStateInternal = {
|
|
101
|
+
...this.loadStateInternal,
|
|
102
|
+
completedAt: totalMs,
|
|
103
|
+
isLoading: false,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** Stops tracking the active load session. */
|
|
108
|
+
destroy() {
|
|
109
|
+
this.removeLoadSessionListener?.();
|
|
110
|
+
this.currentLoadSessionInternal.destroy();
|
|
111
|
+
this.completionRequestedAtTick = undefined;
|
|
112
|
+
this.loadStateInternal = undefined;
|
|
113
|
+
}
|
|
114
|
+
/** Tracks updates from the active load session. */
|
|
115
|
+
listenToLoadSession(loadSession) {
|
|
116
|
+
this.removeLoadSessionListener = loadSession.listen(AssetLoadSessionUpdateEvent, (event) => {
|
|
117
|
+
if (event.detail.complete) {
|
|
118
|
+
this.completionRequestedAtTick = this.latestEngineTick;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
this.completionRequestedAtTick = undefined;
|
|
122
|
+
this.loadStateInternal = {
|
|
123
|
+
current: event.detail.current,
|
|
124
|
+
currentResourceName: event.detail.currentResourceName,
|
|
125
|
+
total: event.detail.total,
|
|
126
|
+
completedAt: undefined,
|
|
127
|
+
isLoading: true,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
19
132
|
}
|
|
20
133
|
/**
|
|
21
134
|
* Manages loading, caching, and cleanup of game assets with progress tracking.
|
|
22
135
|
*
|
|
23
136
|
* @category Asset
|
|
24
137
|
*/
|
|
25
|
-
export class AssetLoader
|
|
138
|
+
export class AssetLoader {
|
|
26
139
|
constructor(options = {}) {
|
|
27
|
-
super();
|
|
28
140
|
this.log = options.logger || browserAnthaLogger;
|
|
29
141
|
}
|
|
30
142
|
/** Logs data. This will use the user's provided logger or default to browser logs. */
|
|
31
143
|
log;
|
|
32
144
|
assetCache = new Map();
|
|
145
|
+
loadSessionController = new AssetLoadSessionController();
|
|
146
|
+
/** The active asset-load session. */
|
|
147
|
+
get currentLoadSession() {
|
|
148
|
+
return this.loadSessionController.currentLoadSession;
|
|
149
|
+
}
|
|
150
|
+
/** The active asset-load state. */
|
|
151
|
+
get loadState() {
|
|
152
|
+
return this.loadSessionController.loadState;
|
|
153
|
+
}
|
|
154
|
+
/** Creates and activates a new asset-load session. */
|
|
155
|
+
createLoadSession() {
|
|
156
|
+
return this.loadSessionController.createLoadSession();
|
|
157
|
+
}
|
|
158
|
+
/** Advances asset-load completion after an engine render. */
|
|
159
|
+
advanceLoadState({ currentTick, totalMs, }) {
|
|
160
|
+
this.loadSessionController.advance({
|
|
161
|
+
currentTick,
|
|
162
|
+
totalMs,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
33
165
|
/** Loads a single asset, returning its cached value if already loaded. */
|
|
34
|
-
async loadIndividualAsset({ asset, incrementProgressCallback, }) {
|
|
166
|
+
async loadIndividualAsset({ asset, incrementProgressCallback, loadSession, }) {
|
|
35
167
|
const cached = this.assetCache.get(asset);
|
|
36
168
|
if (cached) {
|
|
37
169
|
const assetResult = await cached;
|
|
@@ -39,9 +171,18 @@ export class AssetLoader extends ListenTarget {
|
|
|
39
171
|
}
|
|
40
172
|
const deferredLoadPromise = new DeferredPromise();
|
|
41
173
|
this.assetCache.set(asset, deferredLoadPromise.promise);
|
|
174
|
+
loadSession?.reportProgress({
|
|
175
|
+
current: 0,
|
|
176
|
+
currentResourceName: asset.name,
|
|
177
|
+
total: asset.maxProgress,
|
|
178
|
+
});
|
|
42
179
|
const loadedAsset = await asset.load({
|
|
43
180
|
incrementProgressCallback(progressParams) {
|
|
44
181
|
incrementProgressCallback?.(progressParams);
|
|
182
|
+
loadSession?.incrementProgress({
|
|
183
|
+
amount: progressParams,
|
|
184
|
+
currentResourceName: asset.name,
|
|
185
|
+
});
|
|
45
186
|
},
|
|
46
187
|
});
|
|
47
188
|
deferredLoadPromise.resolve(loadedAsset);
|
|
@@ -62,8 +203,9 @@ export class AssetLoader extends ListenTarget {
|
|
|
62
203
|
this.assetCache.delete(asset);
|
|
63
204
|
});
|
|
64
205
|
}
|
|
206
|
+
/** Cleans up cached assets and stops tracking the active load session. */
|
|
65
207
|
async destroy() {
|
|
66
|
-
|
|
208
|
+
this.loadSessionController.destroy();
|
|
67
209
|
const entries = Array.from(this.assetCache.entries());
|
|
68
210
|
await awaitedForEach(entries, async ([asset, result,]) => {
|
|
69
211
|
await (await result).cleanup?.();
|
|
@@ -85,12 +227,14 @@ export class AssetLoader extends ListenTarget {
|
|
|
85
227
|
return count + asset.maxProgress;
|
|
86
228
|
}, 0) + cleanupCount;
|
|
87
229
|
let currentProgress = 0;
|
|
88
|
-
if (
|
|
89
|
-
this.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
230
|
+
if (assetsToLoad.length) {
|
|
231
|
+
this.reportProgress({
|
|
232
|
+
loadSession: options.loadSession,
|
|
233
|
+
progress: {
|
|
234
|
+
current: currentProgress,
|
|
235
|
+
total: maxProgress,
|
|
236
|
+
currentResourceName: assetsToLoad[0]?.name,
|
|
237
|
+
},
|
|
94
238
|
});
|
|
95
239
|
}
|
|
96
240
|
await this.unloadAssets(assetsToCleanup);
|
|
@@ -98,14 +242,14 @@ export class AssetLoader extends ListenTarget {
|
|
|
98
242
|
return assets.map((asset) => this.assetCache.get(asset));
|
|
99
243
|
}
|
|
100
244
|
currentProgress += cleanupCount;
|
|
101
|
-
|
|
102
|
-
|
|
245
|
+
this.reportProgress({
|
|
246
|
+
loadSession: options.loadSession,
|
|
247
|
+
progress: {
|
|
103
248
|
current: currentProgress,
|
|
104
249
|
total: maxProgress,
|
|
105
250
|
currentResourceName: assetsToLoad[0]?.name,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
251
|
+
},
|
|
252
|
+
});
|
|
109
253
|
const chunkedAssets = options.maxParallelism
|
|
110
254
|
? chunkArray(assets, {
|
|
111
255
|
chunkSize: options.maxParallelism,
|
|
@@ -114,14 +258,14 @@ export class AssetLoader extends ListenTarget {
|
|
|
114
258
|
const createIncrementProgressCallback = (asset) => {
|
|
115
259
|
return (amount) => {
|
|
116
260
|
currentProgress += amount ?? 1;
|
|
117
|
-
|
|
118
|
-
|
|
261
|
+
this.reportProgress({
|
|
262
|
+
loadSession: options.loadSession,
|
|
263
|
+
progress: {
|
|
119
264
|
current: currentProgress,
|
|
120
265
|
total: maxProgress,
|
|
121
266
|
currentResourceName: asset.name,
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
267
|
+
},
|
|
268
|
+
});
|
|
125
269
|
};
|
|
126
270
|
};
|
|
127
271
|
const results = (await awaitedBlockingMap(chunkedAssets, async (assetChunk) => {
|
|
@@ -129,14 +273,14 @@ export class AssetLoader extends ListenTarget {
|
|
|
129
273
|
if (this.assetCache.has(asset)) {
|
|
130
274
|
return (await this.assetCache.get(asset))?.value;
|
|
131
275
|
}
|
|
132
|
-
|
|
133
|
-
|
|
276
|
+
this.reportProgress({
|
|
277
|
+
loadSession: options.loadSession,
|
|
278
|
+
progress: {
|
|
134
279
|
current: currentProgress,
|
|
135
280
|
total: maxProgress,
|
|
136
281
|
currentResourceName: asset.name,
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
282
|
+
},
|
|
283
|
+
});
|
|
140
284
|
return await this.loadIndividualAsset({
|
|
141
285
|
incrementProgressCallback: createIncrementProgressCallback(asset),
|
|
142
286
|
asset,
|
|
@@ -156,20 +300,10 @@ export class AssetLoader extends ListenTarget {
|
|
|
156
300
|
},
|
|
157
301
|
});
|
|
158
302
|
}
|
|
159
|
-
if (!options.hideLoadingScreen) {
|
|
160
|
-
this.dispatchProgressUpdate({
|
|
161
|
-
current: maxProgress,
|
|
162
|
-
total: maxProgress,
|
|
163
|
-
currentResourceName: assetsToLoad[assetsToLoad.length - 1]?.name,
|
|
164
|
-
complete: true,
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
303
|
return results;
|
|
168
304
|
}
|
|
169
|
-
/**
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
detail,
|
|
173
|
-
}));
|
|
305
|
+
/** Sends load progress to the provided session. */
|
|
306
|
+
reportProgress({ loadSession, progress, }) {
|
|
307
|
+
loadSession?.reportProgress(progress);
|
|
174
308
|
}
|
|
175
309
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antha/asset",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "An Antha mod for handling asset loading.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vir",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"pixi.js": "^8.19.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@antha/engine": "^0.
|
|
51
|
+
"@antha/engine": "^0.4.1",
|
|
52
52
|
"element-vir": ">=26"
|
|
53
53
|
},
|
|
54
54
|
"engines": {
|