This commit is contained in:
roxy 2026-05-16 02:45:18 +08:00
parent 671f925232
commit 4d0b21aa73
3 changed files with 332 additions and 37 deletions

View File

@ -560,7 +560,6 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
///
_floatingGiftListener(Msg msg) {
final rtcProvider = Provider.of<RtcProvider>(context, listen: false);
rtcProvider.scheduleRoomRocketStatusRefreshForGift();
if (!rtcProvider.shouldShowRoomVisualEffects) {
return;
}

View File

@ -304,6 +304,12 @@ class RealTimeCommunicationManager extends ChangeNotifier {
Duration(seconds: 5),
Duration(seconds: 10),
];
static const List<Duration> _roomRocketEntryReadyFallbackDelays = [
Duration(milliseconds: 300),
Duration(milliseconds: 900),
Duration(milliseconds: 1800),
Duration(milliseconds: 3200),
];
static const int _roomPasswordNotTrueErrorCode = 9005;
static const Duration _micListPollingInterval = Duration(seconds: 2);
@ -345,6 +351,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
Timer? _roomRocketLaunchAnimationTimer;
Timer? _roomRocketGiftRefreshTimer;
final List<Timer> _roomRocketPostLaunchRefreshTimers = [];
final List<Timer> _roomRocketEntryReadyFallbackTimers = [];
Timer? _roomRedPacketPresenceTimer;
Future<void>? _rtcEnginePrewarmTask;
Future<void>? _pendingRoomSwitchRtcLeaveTask;
@ -492,6 +499,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
_roomRocketLaunchAnimationTimer = null;
_roomRocketGiftRefreshTimer?.cancel();
_roomRocketGiftRefreshTimer = null;
_cancelRoomRocketEntryReadyFallbacks();
_cancelRoomRocketPostLaunchStatusRefresh();
}
_roomVisualEffectsEnabled = enabled;
@ -503,7 +511,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
if (visible) {
final roomId = (currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
if (roomId.isNotEmpty) {
rtmProvider?.handleVoiceRoomReadyForRocketRewards(roomId);
_scheduleRoomRocketEntryReadyFallback(roomId);
}
}
}
@ -2600,6 +2608,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
setRoomVisualEffectsEnabled(true);
if (shouldOpenRoomPage) {
VoiceRoomRoute.openVoiceRoom(context!);
setVoiceRoomRouteVisible(true);
}
unawaited(
_bootstrapEnteredVoiceRoomSession(
@ -2700,7 +2709,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
roomId: roomId,
);
_loadRoomSecondaryData();
rtmProvider?.handleVoiceRoomReadyForRocketRewards(roomId);
_scheduleRoomRocketEntryReadyFallback(roomId);
} catch (error, stackTrace) {
if (!_isActiveRoomStartup(entryRequestSerial, roomId)) {
return;
@ -2822,6 +2831,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
roomRocketStatus = res;
notifyListeners();
_scheduleRoomRocketAssetPreload(roomId, res, afterRoomEntry: true);
_scheduleRoomRocketEntryReadyFallback(roomId);
})
.catchError((e) {});
_loadRoomRocketRewardPopups(roomId);
@ -2944,6 +2954,42 @@ class RealTimeCommunicationManager extends ChangeNotifier {
});
}
void _scheduleRoomRocketEntryReadyFallback(String roomId) {
final normalizedRoomId = roomId.trim();
if (normalizedRoomId.isEmpty) {
return;
}
_notifyRoomRocketEntryReadyIfCurrent(normalizedRoomId);
for (final delay in _roomRocketEntryReadyFallbackDelays) {
final timer = Timer(delay, () {
_roomRocketEntryReadyFallbackTimers.removeWhere(
(item) => !item.isActive,
);
_notifyRoomRocketEntryReadyIfCurrent(normalizedRoomId);
});
_roomRocketEntryReadyFallbackTimers.add(timer);
}
}
void _notifyRoomRocketEntryReadyIfCurrent(String roomId) {
final normalizedRoomId = roomId.trim();
final currentRoomId =
currenRoom?.roomProfile?.roomProfile?.id?.trim() ?? "";
if (normalizedRoomId.isEmpty ||
currentRoomId != normalizedRoomId ||
!_voiceRoomRouteVisible) {
return;
}
rtmProvider?.handleVoiceRoomReadyForRocketRewards(normalizedRoomId);
}
void _cancelRoomRocketEntryReadyFallbacks() {
for (final timer in _roomRocketEntryReadyFallbackTimers) {
timer.cancel();
}
_roomRocketEntryReadyFallbackTimers.clear();
}
void scheduleRoomRocketPostLaunchStatusRefresh({String? roomId}) {
final resolvedRoomId =
(roomId ?? currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
@ -3033,7 +3079,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
builder:
(_) => RoomRocketPagEffectOverlay(
resource: animationUrl,
topAvatarUrl: _roomRocketTop1AvatarUrl(),
topAvatarUrl: _roomRocketTop1AvatarUrl(launch.safeLevel),
onCompleted: () {
SmartDialog.dismiss(tag: _roomRocketPagLaunchDialogTag);
_roomRocketLaunchAnimationTimer?.cancel();
@ -3054,8 +3100,12 @@ class RealTimeCommunicationManager extends ChangeNotifier {
);
}
String? _roomRocketTop1AvatarUrl() {
final kings = roomRocketStatus?.rocketKings ?? const [];
String? _roomRocketTop1AvatarUrl(int level) {
final levelKings = roomRocketStatus?.rocketKingsForLevel(level) ?? const [];
final kings =
levelKings.isNotEmpty
? levelKings
: roomRocketStatus?.rocketKings ?? const [];
if (kings.isEmpty) {
return null;
}
@ -3374,9 +3424,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
final exitRtmProvider = rtmProvider;
final navigationContext = context;
try {
exitRtmProvider?.msgAllListener = null;
exitRtmProvider?.msgChatListener = null;
exitRtmProvider?.msgGiftListener = null;
SCGiftVapSvgaManager().stopPlayback();
setRoomVisualEffectsEnabled(false);
SCHeartbeatUtils.cancelTimer();
@ -3412,9 +3459,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
final isPreviewOnlyExit = _currentRoomIsEntryPreview;
final exitRtmProvider = rtmProvider;
try {
exitRtmProvider?.msgAllListener = null;
exitRtmProvider?.msgChatListener = null;
exitRtmProvider?.msgGiftListener = null;
SCGiftVapSvgaManager().stopPlayback();
setRoomVisualEffectsEnabled(false);
SCHeartbeatUtils.cancelTimer();

View File

@ -135,6 +135,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
static const int _roomRocketRegionBroadcastRecentTtlMs = 90000;
static const int _roomRocketRewardPendingTtlMs = 120000;
static const int _roomRocketRewardProbeTtlMs = 120000;
static const int _roomRocketLaunchCountdownRetainMs = 10000;
static const Duration _createRoomGroupTimeout = Duration(seconds: 12);
static const List<Duration> _roomRocketRewardPopupRetryDelays = [
Duration(milliseconds: 700),
@ -262,6 +263,12 @@ class RealTimeMessagingManager extends ChangeNotifier {
final _pendingRoomRocketRewardPopups =
<String, _PendingRoomRocketRewardPopup>{};
final Map<String, int> _recentRoomRocketRewardProbeTimes = <String, int>{};
final Map<String, int> _roomRocketLaunchCountdownDueTimes = <String, int>{};
final Map<String, String> _roomRocketLaunchCountdownKeysByRoom =
<String, String>{};
final _pendingRoomRocketCountdownLaunches =
<String, RoomRocketLaunchBroadcastMessage>{};
final Map<String, Timer> _roomRocketLaunchEffectTimers = <String, Timer>{};
Timer? _roomRocketRewardPopupRetryTimer;
Timer? _roomRocketRoomReadyRewardRetryTimer;
int get currentLuckGiftBurstPlaybackToken => _luckGiftPushPlaybackToken;
@ -1163,6 +1170,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
}
addMsg(Msg msg) {
_scheduleRoomRocketStatusRefreshForGiftMessage(msg);
final mergedGiftMsg = _mergeGiftMessageIfNeeded(msg);
if (mergedGiftMsg != null) {
msgAllListener?.call(mergedGiftMsg);
@ -1249,6 +1257,33 @@ class RealTimeMessagingManager extends ChangeNotifier {
}
}
void _scheduleRoomRocketStatusRefreshForGiftMessage(Msg msg) {
if (msg.type != SCRoomMsgType.gift &&
msg.type != SCRoomMsgType.luckGiftAnimOther) {
return;
}
final currentContext = context;
if (currentContext == null || !currentContext.mounted) {
return;
}
final rtcProvider = Provider.of<RealTimeCommunicationManager>(
currentContext,
listen: false,
);
final currentRoomId =
(rtcProvider.currenRoom?.roomProfile?.roomProfile?.id ?? '').trim();
if (currentRoomId.isEmpty) {
return;
}
final messageRoomId = msg.type == SCRoomMsgType.gift ? msg.msg?.trim() : '';
if (messageRoomId != null &&
messageRoomId.isNotEmpty &&
messageRoomId != currentRoomId) {
return;
}
rtcProvider.scheduleRoomRocketStatusRefreshForGift(roomId: currentRoomId);
}
void _cacheRoomRedPacketMessage(Msg msg) {
try {
final decoded = jsonDecode(msg.msg ?? '');
@ -1985,6 +2020,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
publishRegionBroadcast: true,
);
} else {
_rememberRoomRocketLaunchCountdown(launch);
_markRoomRocketRewardProbe(launch.roomId);
_publishRoomRocketRegionBroadcastIfNeeded(launch);
}
@ -2002,6 +2038,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
if (!launch.isValid) {
return;
}
_rememberRoomRocketLaunchCountdown(launch);
var isCurrentRoom = false;
var isCurrentVisibleRoom = false;
@ -2092,14 +2129,22 @@ class RealTimeMessagingManager extends ChangeNotifier {
final pending = _pendingRoomRocketRewardPopups[normalizedRoomId];
final hasProbe =
_recentRoomRocketRewardProbeTimes[normalizedRoomId] != null;
if (pending == null && !hasProbe) {
final hasLaunch =
_pendingRoomRocketCountdownLaunches[normalizedRoomId] != null;
if (pending == null && !hasProbe && !hasLaunch) {
return;
}
if (!_isCurrentVisibleVoiceRoom(normalizedRoomId)) {
_scheduleRoomRocketReadyRewardRetry(normalizedRoomId, attempt);
return;
}
if (pending == null && attempt == 0) {
final resumedLaunch = _resumeRoomRocketLaunchForCurrentRoom(
normalizedRoomId,
);
if (pending == null && !hasProbe) {
return;
}
if (!resumedLaunch && pending == null && attempt == 0) {
_scheduleRoomRocketReadyRewardRetry(normalizedRoomId, attempt);
return;
}
@ -2180,15 +2225,218 @@ class RealTimeMessagingManager extends ChangeNotifier {
_recentRoomRocketRewardProbeTimes.clear();
_roomRocketRoomReadyRewardRetryTimer?.cancel();
_roomRocketRoomReadyRewardRetryTimer = null;
_clearRoomRocketLaunchCountdownState();
}
void _rememberRoomRocketLaunchCountdown(
RoomRocketLaunchBroadcastMessage launch,
) {
if (!launch.isValid) {
return;
}
final now = DateTime.now().millisecondsSinceEpoch;
_pruneRoomRocketLaunchCountdowns(now);
final key = _roomRocketLaunchCountdownKey(launch);
final existingDueAt = _roomRocketLaunchCountdownDueTimes[key];
final dueAt =
existingDueAt != null && existingDueAt > now
? existingDueAt
: now + _roomRocketLaunchCountdownDelay(launch).inMilliseconds;
_roomRocketLaunchCountdownDueTimes[key] = dueAt;
_roomRocketLaunchCountdownKeysByRoom[launch.roomId] = key;
_pendingRoomRocketCountdownLaunches[launch.roomId] = launch;
}
void _scheduleRoomRocketLaunchEffectsAfterCountdown(
RoomRocketLaunchBroadcastMessage launch,
) {
if (!launch.isValid) {
return;
}
_rememberRoomRocketLaunchCountdown(launch);
final key = _roomRocketLaunchCountdownKey(launch);
final delay = _roomRocketLaunchCountdownRemaining(launch.roomId);
final dueAt =
_roomRocketLaunchCountdownDueTimes[key] ??
DateTime.now().millisecondsSinceEpoch;
_roomRocketLaunchEffectTimers[key]?.cancel();
_roomRocketLaunchEffectTimers[key] = Timer(delay, () {
_roomRocketLaunchEffectTimers.remove(key);
if (_roomRocketLaunchCountdownDueTimes[key] != dueAt) {
return;
}
_roomRocketLaunchCountdownDueTimes.remove(key);
if (_roomRocketLaunchCountdownKeysByRoom[launch.roomId] == key) {
_roomRocketLaunchCountdownKeysByRoom.remove(launch.roomId);
}
final pendingLaunch =
_pendingRoomRocketCountdownLaunches.remove(launch.roomId) ?? launch;
_playRoomRocketLaunchEffectsAndReward(pendingLaunch);
});
}
void _playRoomRocketLaunchEffectsAndReward(
RoomRocketLaunchBroadcastMessage launch,
) {
_clearRoomRocketLaunchCountdownForRoom(launch.roomId);
if (!_isCurrentVisibleVoiceRoom(launch.roomId)) {
_markRoomRocketRewardProbe(launch.roomId);
return;
}
final currentContext = context;
if (currentContext == null || !currentContext.mounted) {
_markRoomRocketRewardProbe(launch.roomId);
return;
}
final rtcProvider = Provider.of<RealTimeCommunicationManager>(
currentContext,
listen: false,
);
final shouldFetchReward =
_pendingRoomRocketRewardPopups.containsKey(launch.roomId) ||
_recentRoomRocketRewardProbeTimes.containsKey(launch.roomId);
final pending = _pendingRoomRocketRewardPopups.remove(launch.roomId);
_recentRoomRocketRewardProbeTimes.remove(launch.roomId);
rtcProvider.handleRoomRocketLaunchBroadcast(launch);
if (shouldFetchReward) {
unawaited(
_showRoomRocketRewardPopupDialog(
launch.roomId,
initialRoomRecords:
pending?.initialRoomRecords ??
const <SCRoomRocketRewardRecordRes>[],
),
);
}
}
bool _resumeRoomRocketLaunchForCurrentRoom(String roomId) {
final normalizedRoomId = roomId.trim();
final launch = _pendingRoomRocketCountdownLaunches[normalizedRoomId];
if (launch == null || !_isCurrentVisibleVoiceRoom(normalizedRoomId)) {
return false;
}
final currentContext = context;
if (currentContext == null || !currentContext.mounted) {
return false;
}
final rtcProvider = Provider.of<RealTimeCommunicationManager>(
currentContext,
listen: false,
);
final remaining = _roomRocketLaunchCountdownRemaining(normalizedRoomId);
if (remaining > Duration.zero) {
_addRoomRocketLaunchNoticeIfNeeded(
rtcProvider: rtcProvider,
launch: launch,
secondsOverride: _roomRocketRemainingSeconds(remaining),
);
_scheduleRoomRocketLaunchEffectsAfterCountdown(launch);
} else {
_playRoomRocketLaunchEffectsAndReward(launch);
}
return true;
}
int _roomRocketRemainingSeconds(Duration duration) {
final seconds = (duration.inMilliseconds / 1000).ceil();
return seconds.clamp(1, 60).toInt();
}
Duration _roomRocketLaunchCountdownRemaining(String roomId) {
final normalizedRoomId = roomId.trim();
if (normalizedRoomId.isEmpty) {
return Duration.zero;
}
final now = DateTime.now().millisecondsSinceEpoch;
_pruneRoomRocketLaunchCountdowns(now);
final key = _roomRocketLaunchCountdownKeysByRoom[normalizedRoomId];
final dueAt = key == null ? null : _roomRocketLaunchCountdownDueTimes[key];
if (dueAt == null || dueAt <= now) {
return Duration.zero;
}
return Duration(milliseconds: dueAt - now);
}
Duration _roomRocketLaunchCountdownDelay(
RoomRocketLaunchBroadcastMessage launch,
) {
final seconds = launch.durationSeconds <= 0 ? 10 : launch.durationSeconds;
return Duration(seconds: seconds.clamp(0, 60).toInt());
}
String _roomRocketLaunchCountdownKey(
RoomRocketLaunchBroadcastMessage launch,
) {
final launchKey = _roomRocketLaunchNoticeKey(launch);
if (launchKey.isNotEmpty) {
return launchKey;
}
return 'rocket_launch_countdown|${launch.roomId}';
}
void _pruneRoomRocketLaunchCountdowns(int now) {
final expiredKeys = <String>{};
_roomRocketLaunchCountdownDueTimes.removeWhere((key, dueAt) {
final expired = now - dueAt > _roomRocketLaunchCountdownRetainMs;
if (expired) {
expiredKeys.add(key);
}
return expired;
});
if (expiredKeys.isEmpty) {
return;
}
_roomRocketLaunchCountdownKeysByRoom.removeWhere(
(_, key) => expiredKeys.contains(key),
);
_pendingRoomRocketCountdownLaunches.removeWhere(
(roomId, _) => !_roomRocketLaunchCountdownKeysByRoom.containsKey(roomId),
);
}
void _clearRoomRocketLaunchCountdownState() {
_roomRocketLaunchCountdownDueTimes.clear();
_roomRocketLaunchCountdownKeysByRoom.clear();
_pendingRoomRocketCountdownLaunches.clear();
for (final timer in _roomRocketLaunchEffectTimers.values) {
timer.cancel();
}
_roomRocketLaunchEffectTimers.clear();
}
void _clearRoomRocketLaunchCountdownForRoom(String roomId) {
final normalizedRoomId = roomId.trim();
final key = _roomRocketLaunchCountdownKeysByRoom.remove(normalizedRoomId);
if (key != null) {
_roomRocketLaunchCountdownDueTimes.remove(key);
_roomRocketLaunchEffectTimers.remove(key)?.cancel();
}
_pendingRoomRocketCountdownLaunches.remove(normalizedRoomId);
}
void _handleCurrentVisibleRoomRocketLaunch({
required RealTimeCommunicationManager rtcProvider,
required RoomRocketLaunchBroadcastMessage launch,
bool publishRegionBroadcast = false,
}) {
if (_addRoomRocketLaunchNoticeIfNeeded(
rtcProvider: rtcProvider,
launch: launch,
publishRegionBroadcast: publishRegionBroadcast,
)) {
_scheduleRoomRocketLaunchEffectsAfterCountdown(launch);
}
}
bool _addRoomRocketLaunchNoticeIfNeeded({
required RealTimeCommunicationManager rtcProvider,
required RoomRocketLaunchBroadcastMessage launch,
bool publishRegionBroadcast = false,
int? secondsOverride,
}) {
if (!_isCurrentVisibleVoiceRoom(launch.roomId)) {
return;
return false;
}
final launchKey = _roomRocketLaunchNoticeKey(launch);
final looseLaunchKey = _roomRocketLaunchLooseKey(launch);
@ -2199,26 +2447,27 @@ class RealTimeMessagingManager extends ChangeNotifier {
_recentRoomRocketLaunchTimes.containsKey(launchKey)) ||
(looseLaunchKey.isNotEmpty &&
_recentRoomRocketLaunchLooseTimes.containsKey(looseLaunchKey));
if (!alreadyHandled && launchKey.isNotEmpty) {
_recentRoomRocketLaunchTimes[launchKey] = now;
if (looseLaunchKey.isNotEmpty) {
_recentRoomRocketLaunchLooseTimes[looseLaunchKey] = now;
}
addMsg(
_buildRoomRocketLaunchNoticeMsg(
_firstNonBlank([
rtcProvider.currenRoom?.roomProfile?.roomProfile?.roomAccount,
launch.roomAccount,
]),
launch,
),
);
if (publishRegionBroadcast) {
_publishRoomRocketRegionBroadcastIfNeeded(launch);
}
rtcProvider.handleRoomRocketLaunchBroadcast(launch);
if (alreadyHandled || launchKey.isEmpty) {
return false;
}
unawaited(_showRoomRocketRewardPopupDialog(launch.roomId));
_recentRoomRocketLaunchTimes[launchKey] = now;
if (looseLaunchKey.isNotEmpty) {
_recentRoomRocketLaunchLooseTimes[looseLaunchKey] = now;
}
addMsg(
_buildRoomRocketLaunchNoticeMsg(
_firstNonBlank([
rtcProvider.currenRoom?.roomProfile?.roomProfile?.roomAccount,
launch.roomAccount,
]),
launch,
secondsOverride: secondsOverride,
),
);
if (publishRegionBroadcast) {
_publishRoomRocketRegionBroadcastIfNeeded(launch);
}
return true;
}
void _publishRoomRocketRegionBroadcastIfNeeded(
@ -2453,9 +2702,12 @@ class RealTimeMessagingManager extends ChangeNotifier {
Msg _buildRoomRocketLaunchNoticeMsg(
String groupId,
RoomRocketLaunchBroadcastMessage launch,
) {
final seconds = launch.durationSeconds <= 0 ? 10 : launch.durationSeconds;
RoomRocketLaunchBroadcastMessage launch, {
int? secondsOverride,
}) {
final seconds =
secondsOverride ??
(launch.durationSeconds <= 0 ? 10 : launch.durationSeconds);
return Msg(
groupId: groupId,
msg: seconds.toString(),