Initial flame_lua_runtime package
This commit is contained in:
186
lib/runtime/resources/game_resource_loading.dart
Normal file
186
lib/runtime/resources/game_resource_loading.dart
Normal file
@@ -0,0 +1,186 @@
|
||||
part of 'game_resource_manager.dart';
|
||||
|
||||
extension _GameResourceManagerLoading on GameResourceManager {
|
||||
Future<ui.Image?> _loadImage(
|
||||
String? keyOrPath, {
|
||||
required bool failOnError,
|
||||
bool retain = false,
|
||||
}) {
|
||||
if (keyOrPath == null || keyOrPath.isEmpty) {
|
||||
return Future.value(null);
|
||||
}
|
||||
|
||||
final requestToken = _asyncGate.token;
|
||||
final requestGeneration = requestToken.generation;
|
||||
final path = _tryResolve(keyOrPath);
|
||||
if (path == null) {
|
||||
return Future.value(null);
|
||||
}
|
||||
|
||||
final existing = _images[path];
|
||||
if (existing != null) {
|
||||
final image = existing.image;
|
||||
if (existing.generation == requestGeneration &&
|
||||
existing.state == GameResourceState.ready &&
|
||||
image != null) {
|
||||
if (retain) {
|
||||
existing.refCount++;
|
||||
}
|
||||
_touch(existing);
|
||||
return Future.value(image);
|
||||
}
|
||||
final inflight = existing.inflight;
|
||||
if (existing.generation == requestGeneration && inflight != null) {
|
||||
return failOnError
|
||||
? _throwIfNull(inflight, keyOrPath)
|
||||
: inflight.catchError((_) => null);
|
||||
}
|
||||
}
|
||||
|
||||
final record = _ImageResourceRecord(generation: requestGeneration)
|
||||
..state = GameResourceState.loading;
|
||||
_images[path] = record;
|
||||
|
||||
final future = _decodeImage(path, record, requestToken, retain: retain);
|
||||
record.inflight = future;
|
||||
return failOnError ? _throwIfNull(future, keyOrPath) : future;
|
||||
}
|
||||
|
||||
Future<ui.Image?> _throwIfNull(
|
||||
Future<ui.Image?> future,
|
||||
String keyOrPath,
|
||||
) async {
|
||||
final image = await future;
|
||||
if (image == null) {
|
||||
throw ResourceLoadException('Required image resource failed: $keyOrPath');
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
Future<ui.Image?> _decodeImage(
|
||||
String path,
|
||||
_ImageResourceRecord record,
|
||||
RuntimeAsyncToken requestToken, {
|
||||
required bool retain,
|
||||
}) async {
|
||||
try {
|
||||
final activePackage = _package;
|
||||
if (activePackage == null) {
|
||||
throw StateError('GameResourceManager has no active package');
|
||||
}
|
||||
|
||||
final frame = await _loadLimiter.run(() async {
|
||||
final bytes = await activePackage.readBytes(path);
|
||||
final codec = await ui.instantiateImageCodec(
|
||||
bytes.buffer.asUint8List(),
|
||||
);
|
||||
return codec.getNextFrame();
|
||||
});
|
||||
record.inflight = null;
|
||||
|
||||
if (!_asyncGate.accepts(requestToken) || _images[path] != record) {
|
||||
frame.image.dispose();
|
||||
record.state = GameResourceState.disposed;
|
||||
return null;
|
||||
}
|
||||
|
||||
record
|
||||
..image = frame.image
|
||||
..estimatedBytes = frame.image.width * frame.image.height * 4
|
||||
..state = GameResourceState.ready
|
||||
..lastError = null;
|
||||
if (retain) {
|
||||
record.refCount++;
|
||||
}
|
||||
_cacheBytes += record.estimatedBytes;
|
||||
_touch(record);
|
||||
_enforceImageBudget();
|
||||
return frame.image;
|
||||
} catch (error) {
|
||||
record.inflight = null;
|
||||
if (!_asyncGate.accepts(requestToken) || _images[path] != record) {
|
||||
record.state = GameResourceState.disposed;
|
||||
return null;
|
||||
}
|
||||
record
|
||||
..state = GameResourceState.failed
|
||||
..lastError = error;
|
||||
_diagnostics?.record(
|
||||
type: RuntimeDiagnosticType.resourceLoadError,
|
||||
message: 'Image resource failed to load',
|
||||
error: error,
|
||||
context: {'path': path, 'generation': requestToken.generation},
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _preloadSpine(
|
||||
String keyOrPath, {
|
||||
required bool failOnError,
|
||||
}) async {
|
||||
final spine = await _createSpineComponent(keyOrPath);
|
||||
spine?.dispose();
|
||||
if (failOnError && spine == null) {
|
||||
throw ResourceLoadException('Required spine resource failed: $keyOrPath');
|
||||
}
|
||||
}
|
||||
|
||||
Future<SpineComponent?> _createSpineComponent(String? keyOrPath) async {
|
||||
if (keyOrPath == null || keyOrPath.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
final requestToken = _asyncGate.token;
|
||||
final activePackage = _package;
|
||||
if (activePackage == null) {
|
||||
return null;
|
||||
}
|
||||
final resource = activePackage.manifest.resources[keyOrPath];
|
||||
if (resource == null || resource.type != GameResourceType.spine) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
await initSpineFlutter();
|
||||
final atlasPath = activePackage.resolveResourcePath(resource.atlas!);
|
||||
final skeletonPath = activePackage.resolveResourcePath(
|
||||
resource.skeleton!,
|
||||
);
|
||||
final drawable = await _loadLimiter.run(() {
|
||||
return SkeletonDrawableFlutter.fromMemory(atlasPath, skeletonPath, (
|
||||
name,
|
||||
) async {
|
||||
final bytes = await activePackage.readBytes(name);
|
||||
return bytes.buffer.asUint8List(
|
||||
bytes.offsetInBytes,
|
||||
bytes.lengthInBytes,
|
||||
);
|
||||
});
|
||||
});
|
||||
if (!_asyncGate.accepts(requestToken)) {
|
||||
drawable.dispose();
|
||||
return null;
|
||||
}
|
||||
return SpineComponent(drawable);
|
||||
} catch (error) {
|
||||
if (!_asyncGate.accepts(requestToken)) {
|
||||
return null;
|
||||
}
|
||||
_diagnostics?.record(
|
||||
type: RuntimeDiagnosticType.resourceLoadError,
|
||||
message: 'Spine resource failed to load',
|
||||
error: error,
|
||||
context: {'key': keyOrPath, 'generation': requestToken.generation},
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String? _tryResolve(String keyOrPath) {
|
||||
try {
|
||||
return resolve(keyOrPath);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user