82 lines
3.0 KiB
Dart
82 lines
3.0 KiB
Dart
import 'dart:ui' as ui;
|
|
|
|
import 'package:dio/dio.dart';
|
|
import 'package:flame/components.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
import '../config.dart';
|
|
|
|
/// بارگذاریِ تصاویرِ کارت از backend (نه bundle داخل اپ) تا حجم اپ کم بماند.
|
|
/// هر اسکین یک پوشه روی سرور دارد: `/cards/<skin>/<code>.png` و `/cards/<skin>/back.jpg`.
|
|
/// تصاویرِ دانلودشده در حافظه کش میشوند تا هر کارت فقط یکبار از شبکه بیاید.
|
|
class CardImages {
|
|
CardImages._();
|
|
|
|
static const _fallbackSkin = 'simple';
|
|
static final Dio _dio = Dio(BaseOptions(
|
|
responseType: ResponseType.bytes,
|
|
connectTimeout: const Duration(seconds: 10),
|
|
receiveTimeout: const Duration(seconds: 12),
|
|
validateStatus: (s) => s != null && s >= 200 && s < 300,
|
|
));
|
|
static final Map<String, Future<Sprite?>> _cache = {};
|
|
|
|
static String _url(String skin, String file) =>
|
|
'${AppConfig.baseUrl}/cards/$skin/$file';
|
|
|
|
/// اسپرایتِ رویِ یک کارت (مثل "AS"). در صورت خطا/۴۰۴ به اسکینِ پیشفرض و
|
|
/// سپس به `null` (کشیدنِ برداری در CardComponent) برمیگردد.
|
|
static Future<Sprite?> front(String skin, String code) =>
|
|
_load(skin, '$code.png');
|
|
|
|
/// اسپرایتِ پشتِ کارت برای اسکین دادهشده.
|
|
static Future<Sprite?> back(String skin) => _load(skin, 'back.jpg');
|
|
|
|
static Future<Sprite?> _load(String skin, String file) {
|
|
final key = '$skin/$file';
|
|
return _cache.putIfAbsent(key, () => _fetch(skin, file));
|
|
}
|
|
|
|
static Future<Sprite?> _fetch(String skin, String file) async {
|
|
final sprite = await _fetchFrom(skin, file);
|
|
if (sprite != null) return sprite;
|
|
if (skin != _fallbackSkin) return _fetchFrom(_fallbackSkin, file);
|
|
return null;
|
|
}
|
|
|
|
static Future<Sprite?> _fetchFrom(String skin, String file) async {
|
|
try {
|
|
final res = await _dio.get<List<int>>(_url(skin, file));
|
|
final data = res.data;
|
|
if (data == null || data.isEmpty) return null;
|
|
final image = await _decode(Uint8List.fromList(data));
|
|
return Sprite(image);
|
|
} catch (e) {
|
|
debugPrint('CardImages: $skin/$file failed: $e');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// تصویرِ فرش (برای سطحِ میز) از URL کاملِ backend. کششده با کلیدِ URL.
|
|
static Future<Sprite?> carpetByUrl(String url) {
|
|
if (url.isEmpty) return Future.value(null);
|
|
return _cache.putIfAbsent('carpet/$url', () async {
|
|
try {
|
|
final res = await _dio.get<List<int>>(url);
|
|
final data = res.data;
|
|
if (data == null || data.isEmpty) return null;
|
|
return Sprite(await _decode(Uint8List.fromList(data)));
|
|
} catch (e) {
|
|
debugPrint('CardImages: carpet $url failed: $e');
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
|
|
static Future<ui.Image> _decode(Uint8List bytes) async {
|
|
final codec = await ui.instantiateImageCodec(bytes);
|
|
final frame = await codec.getNextFrame();
|
|
return frame.image;
|
|
}
|
|
}
|