diff --git a/YuMi/Assets.xcassets/20.20.62/turbo_mode_tips.imageset/Contents.json b/YuMi/Assets.xcassets/20.20.62/turbo_mode_tips.imageset/Contents.json new file mode 100644 index 00000000..0f2ba59d --- /dev/null +++ b/YuMi/Assets.xcassets/20.20.62/turbo_mode_tips.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "蒙版组 1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/YuMi/Assets.xcassets/20.20.62/turbo_mode_tips.imageset/蒙版组 1@3x.png b/YuMi/Assets.xcassets/20.20.62/turbo_mode_tips.imageset/蒙版组 1@3x.png new file mode 100644 index 00000000..06c6ef65 Binary files /dev/null and b/YuMi/Assets.xcassets/20.20.62/turbo_mode_tips.imageset/蒙版组 1@3x.png differ diff --git a/YuMi/Global/BuglyManager.h b/YuMi/Global/BuglyManager.h index 968207f7..78dc5c7d 100644 --- a/YuMi/Global/BuglyManager.h +++ b/YuMi/Global/BuglyManager.h @@ -110,6 +110,12 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)startLagDetection; +/** + * 模拟卡顿检测(测试用) + * 用于测试按钮,直接增加计数 + */ +- (void)simulateLagDetection; + @end NS_ASSUME_NONNULL_END diff --git a/YuMi/Global/BuglyManager.m b/YuMi/Global/BuglyManager.m index 8faf4031..eab98b27 100644 --- a/YuMi/Global/BuglyManager.m +++ b/YuMi/Global/BuglyManager.m @@ -8,12 +8,18 @@ #import "BuglyManager.h" #import +#import "TurboModeStateManager.h" @interface BuglyManager () @property (nonatomic, strong) NSString *appId; @property (nonatomic, assign) BOOL isConfigured; +// 卡顿计数相关 +@property (nonatomic, assign) NSInteger lagCount; +@property (nonatomic, assign) BOOL isInRoom; +@property (nonatomic, assign) BOOL isTurboModeEnabled; + @end @implementation BuglyManager @@ -33,6 +39,15 @@ self = [super init]; if (self) { _isConfigured = NO; + _lagCount = 0; + _isInRoom = NO; + _isTurboModeEnabled = NO; + + // 监听 turbo mode 状态变化 + [self setupTurboModeNotifications]; + + // 🔧 修复:初始化时获取当前 turbo mode 状态 + [self updateTurboModeState]; } return self; } @@ -212,6 +227,15 @@ // Bugly 会自动进行卡顿检测,这里主要是日志记录 } +#pragma mark - 测试方法 + +- (void)simulateLagDetection { + NSLog(@"[BuglyManager] 🧪 模拟卡顿检测(测试按钮触发)"); + + // 模拟卡顿检测,触发计数逻辑 + [self handleLagCountWithDuration:3.0 stackTrace:@"模拟卡顿 - 测试按钮触发"]; +} + #pragma mark - Private Methods - (void)handleLagDetection:(NSString *)stackTrace { @@ -227,13 +251,8 @@ }); } - // 🔧 新增:发送卡顿检测通知,用于在房间中显示 Turbo Mode Tips - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:@"BuglyManagerDidDetectLag" - object:nil - userInfo:@{@"duration": @(duration), - @"stackTrace": stackTrace ?: @""}]; - }); + // 🔧 新增:卡顿计数逻辑 + [self handleLagCountWithDuration:duration stackTrace:stackTrace]; // TODO: 触发卡顿通知逻辑 // 1. 记录卡顿信息到本地日志 @@ -242,12 +261,135 @@ // 4. 触发用户反馈机制 } +#pragma mark - 卡顿计数逻辑 + +- (void)handleLagCountWithDuration:(NSTimeInterval)duration stackTrace:(NSString *)stackTrace { + // 规则2:当 turbo mode 开启时,不计数 + if (self.isTurboModeEnabled) { + NSLog(@"[BuglyManager] 🎮 Turbo Mode 已开启,跳过卡顿计数"); + return; + } + + // 规则1:只有在房间内才计数 + if (!self.isInRoom) { + NSLog(@"[BuglyManager] 🏠 不在房间内,跳过卡顿计数"); + return; + } + + // 增加计数 + self.lagCount++; + NSLog(@"[BuglyManager] 📊 卡顿计数: %ld/3", (long)self.lagCount); + + // 检查是否达到3次 + if (self.lagCount >= 3) { + NSLog(@"[BuglyManager] 🚨 累计卡顿3次,触发 Turbo Mode Tips"); + + // 发送通知给 Tips Manager + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:@"BuglyManagerDidDetectLag" + object:nil + userInfo:@{@"duration": @(duration), + @"stackTrace": stackTrace ?: @"", + @"lagCount": @(self.lagCount), + @"shouldShowTips": @YES}]; + }); + + // 重置计数 + self.lagCount = 0; + } else { + // 未达到3次,只发送计数通知 + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:@"BuglyManagerDidDetectLag" + object:nil + userInfo:@{@"duration": @(duration), + @"stackTrace": stackTrace ?: @"", + @"lagCount": @(self.lagCount), + @"shouldShowTips": @NO}]; + }); + } +} + +- (void)setupTurboModeNotifications { + // 监听 turbo mode 状态变化 + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(turboModeStateChanged:) + name:@"TurboModeStateChanged" + object:nil]; + + // 监听房间进入/退出 + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(roomDidEnter:) + name:@"RoomDidEnter" + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(roomDidExit:) + name:@"RoomDidExit" + object:nil]; +} + +- (void)turboModeStateChanged:(NSNotification *)notification { + NSNumber *enabled = notification.userInfo[@"enabled"]; + if (enabled) { + self.isTurboModeEnabled = [enabled boolValue]; + NSLog(@"[BuglyManager] 🎮 Turbo Mode 状态变化: %@", enabled.boolValue ? @"开启" : @"关闭"); + + // 规则2:当 turbo mode 开启时,计数复位为0 + if (enabled.boolValue) { + self.lagCount = 0; + NSLog(@"[BuglyManager] 🔄 Turbo Mode 开启,卡顿计数复位为0"); + } + } +} + +- (void)roomDidEnter:(NSNotification *)notification { + self.isInRoom = YES; + + // 🔧 修复:进入房间时主动获取当前 turbo mode 状态 + [self updateTurboModeState]; + + NSLog(@"[BuglyManager] 🏠 用户进入房间,开始卡顿监控 - Turbo Mode: %@", + self.isTurboModeEnabled ? @"开启" : @"关闭"); +} + +- (void)roomDidExit:(NSNotification *)notification { + self.isInRoom = NO; + // 规则3:当用户退出房间时,计数复位为0 + self.lagCount = 0; + NSLog(@"[BuglyManager] 🚪 用户退出房间,卡顿计数复位为0"); +} + +#pragma mark - 状态更新方法 + +- (void)updateTurboModeState { + // 从 TurboModeStateManager 获取当前状态 + BOOL currentTurboModeState = [[TurboModeStateManager sharedManager] isTurboModeEnabled]; + + if (self.isTurboModeEnabled != currentTurboModeState) { + self.isTurboModeEnabled = currentTurboModeState; + NSLog(@"[BuglyManager] 🔄 主动更新 Turbo Mode 状态: %@", + currentTurboModeState ? @"开启" : @"关闭"); + + // 如果 turbo mode 开启,计数复位为0 + if (currentTurboModeState) { + self.lagCount = 0; + NSLog(@"[BuglyManager] 🔄 Turbo Mode 开启,卡顿计数复位为0"); + } + } +} + - (NSString *)getAppChannel { // 这里应该调用项目中的工具方法获取渠道信息 // 暂时返回默认值 return @"AppStore"; } +#pragma mark - Dealloc + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + - (NSString *)getIAPStatusMessage:(NSInteger)status { switch (status) { case 0: diff --git a/YuMi/Modules/YMRoom/Manager/PublicRoomManager.m b/YuMi/Modules/YMRoom/Manager/PublicRoomManager.m index 1ea3d962..eb026fb8 100644 --- a/YuMi/Modules/YMRoom/Manager/PublicRoomManager.m +++ b/YuMi/Modules/YMRoom/Manager/PublicRoomManager.m @@ -385,7 +385,7 @@ - (void)handleMessageWithAttachmentAndFirstSecond:(NIMMessage *)message { // 只有用户在房间时,才会转发 if (![XPSkillCardPlayerManager shareInstance].isInRoom) { - NSLog(@"PublicRoomManager: 用户未在房间中,跳过消息转发"); +// NSLog(@"PublicRoomManager: 用户未在房间中,跳过消息转发"); return; } [[NSNotificationCenter defaultCenter] postNotificationName:@"MessageFromPublicRoomWithAttachmentNotification" diff --git a/YuMi/Modules/YMRoom/View/AnimationView/RoomAnimationView.m b/YuMi/Modules/YMRoom/View/AnimationView/RoomAnimationView.m index 79272813..0e048b88 100644 --- a/YuMi/Modules/YMRoom/View/AnimationView/RoomAnimationView.m +++ b/YuMi/Modules/YMRoom/View/AnimationView/RoomAnimationView.m @@ -4055,14 +4055,14 @@ BannerSchedulerDelegate #pragma mark - BannerSchedulerDelegate - (void)bannerScheduler:(BannerScheduler *)scheduler shouldPlayBanner:(id)banner { - NSLog(@"🎯 BannerSchedulerDelegate: 收到播放 Banner 请求"); - NSLog(@"🎯 Banner 类型: %@", [banner class]); +// NSLog(@"🎯 BannerSchedulerDelegate: 收到播放 Banner 请求"); +// NSLog(@"🎯 Banner 类型: %@", [banner class]); // 将 Banner 数据转换为 AttachmentModel 并播放 if ([banner isKindOfClass:[AttachmentModel class]]) { AttachmentModel *attachment = (AttachmentModel *)banner; - NSLog(@"🎯 AttachmentModel 类型: %ld", (long)attachment.second); - NSLog(@"🎯 AttachmentModel 数据: %@", attachment.data); +// NSLog(@"🎯 AttachmentModel 类型: %ld", (long)attachment.second); +// NSLog(@"🎯 AttachmentModel 数据: %@", attachment.data); [self _playBannerWithAttachment:attachment]; } else { NSLog(@"⚠️ BannerSchedulerDelegate: Banner 不是 AttachmentModel 类型"); diff --git a/YuMi/Modules/YMRoom/View/MoreView/Manager/TurboModeStateManager.m b/YuMi/Modules/YMRoom/View/MoreView/Manager/TurboModeStateManager.m index 375ffb3a..3360b25e 100644 --- a/YuMi/Modules/YMRoom/View/MoreView/Manager/TurboModeStateManager.m +++ b/YuMi/Modules/YMRoom/View/MoreView/Manager/TurboModeStateManager.m @@ -43,6 +43,11 @@ [[NSUserDefaults standardUserDefaults] synchronize]; NSLog(@"🎮 TurboModeStateManager: 全局 turbo mode 设置为 %@", enabled ? @"开启" : @"关闭"); + + // 发送状态变化通知 + [[NSNotificationCenter defaultCenter] postNotificationName:@"TurboModeStateChanged" + object:nil + userInfo:@{@"enabled": @(enabled)}]; } - (BOOL)isTurboModeEnabled { diff --git a/YuMi/Modules/YMRoom/View/MoreView/Manager/XPTurboModeTipsManager.h b/YuMi/Modules/YMRoom/View/MoreView/Manager/XPTurboModeTipsManager.h index cd3b28ef..e76f3fbd 100644 --- a/YuMi/Modules/YMRoom/View/MoreView/Manager/XPTurboModeTipsManager.h +++ b/YuMi/Modules/YMRoom/View/MoreView/Manager/XPTurboModeTipsManager.h @@ -24,6 +24,9 @@ NS_ASSUME_NONNULL_BEGIN // 手动显示 Tips(用于测试) - (void)showTipsManually; +// 直接开启 Turbo Mode(用于 Tips View 显示时) +- (void)enableTurboModeDirectly; + @end NS_ASSUME_NONNULL_END diff --git a/YuMi/Modules/YMRoom/View/MoreView/Manager/XPTurboModeTipsManager.m b/YuMi/Modules/YMRoom/View/MoreView/Manager/XPTurboModeTipsManager.m index 75fc61b3..69c34175 100644 --- a/YuMi/Modules/YMRoom/View/MoreView/Manager/XPTurboModeTipsManager.m +++ b/YuMi/Modules/YMRoom/View/MoreView/Manager/XPTurboModeTipsManager.m @@ -8,12 +8,14 @@ #import "XPTurboModeTipsManager.h" #import "XPTurboModeTipsView.h" +#import "TurboModeStateManager.h" @interface XPTurboModeTipsManager () @property (nonatomic, strong) XPTurboModeTipsView *currentTipsView; @property (nonatomic, assign) BOOL isMonitoring; -@property (nonatomic, assign) BOOL hasShownTipsInCurrentSession; +@property (nonatomic, strong) NSString *currentRoomId; // 🔧 新增:保存当前房间ID +//@property (nonatomic, assign) BOOL hasShownTipsInCurrentSession; @end @@ -34,7 +36,6 @@ self = [super init]; if (self) { _isMonitoring = NO; - _hasShownTipsInCurrentSession = NO; [self setupNotifications]; } return self; @@ -49,7 +50,6 @@ } self.isMonitoring = YES; - self.hasShownTipsInCurrentSession = NO; NSLog(@"🎮 XPTurboModeTipsManager 开始在房间中监听卡顿"); } @@ -75,6 +75,12 @@ name:@"BuglyManagerDidDetectLag" object:nil]; + // 🔧 新增:监听房间进入通知,保存房间ID + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleRoomEnter:) + name:@"RoomDidEnter" + object:nil]; + // 监听房间退出通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleRoomExit:) @@ -88,21 +94,36 @@ return; } - if (self.hasShownTipsInCurrentSession) { - NSLog(@"🎮 XPTurboModeTipsManager 当前会话已显示过 Tips,跳过"); - return; - } - NSDictionary *userInfo = notification.userInfo; NSTimeInterval duration = [userInfo[@"duration"] doubleValue]; NSString *stackTrace = userInfo[@"stackTrace"]; + NSNumber *shouldShowTips = userInfo[@"shouldShowTips"]; + NSNumber *lagCount = userInfo[@"lagCount"]; - NSLog(@"🎮 XPTurboModeTipsManager 收到卡顿通知 - 持续时间: %.2f秒", duration); + NSLog(@"🎮 XPTurboModeTipsManager 收到卡顿通知 - 持续时间: %.2f秒, 计数: %@/3", duration, lagCount); - // 延迟显示 Tips,避免在卡顿期间立即显示 - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [self showTipsWithReason:[NSString stringWithFormat:@"检测到卡顿 (%.1fs)", duration]]; - }); + // 检查是否应该显示 Tips(基于新的计数逻辑) + if (shouldShowTips && [shouldShowTips boolValue]) { + NSLog(@"🎮 累计卡顿 %@ 次,显示 Turbo Mode Tips", lagCount); + + // 延迟显示 Tips,避免在卡顿期间立即显示 + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self showTipsWithReason:[NSString stringWithFormat:@"累计卡顿%@次", lagCount]]; + }); + } else { + NSLog(@"🎮 卡顿计数: %@/3,暂不显示 Tips", lagCount); + } +} + +- (void)handleRoomEnter:(NSNotification *)notification { + // 保存当前房间ID + NSString *roomId = notification.userInfo[@"roomId"]; + if (roomId && ![roomId isEqualToString:@"unknown"]) { + self.currentRoomId = roomId; + NSLog(@"🎮 XPTurboModeTipsManager 保存当前房间ID: %@", roomId); + } else { + NSLog(@"🎮 XPTurboModeTipsManager 收到无效的房间ID: %@", roomId); + } } - (void)handleRoomExit:(NSNotification *)notification { @@ -141,9 +162,6 @@ self.currentTipsView = [XPTurboModeTipsView showInView:window]; self.currentTipsView.delegate = self; - // 标记已显示 - self.hasShownTipsInCurrentSession = YES; - NSLog(@"🎮 Tips 弹窗创建成功,已添加到窗口"); } @@ -159,6 +177,74 @@ } } +- (void)turboModeTipsViewDidEnableTurboMode { + NSLog(@"🎮 XPTurboModeTipsManager 用户确认开启 Turbo Mode"); + + // 开启全局 turbo mode + [[TurboModeStateManager sharedManager] setTurboModeEnabled:YES]; + + // 获取当前房间ID(如果有的话) + NSString *currentRoomId = [self getCurrentRoomId]; + if (currentRoomId) { + // 应用 turbo mode 到当前房间的开关 + [[TurboModeStateManager sharedManager] applyTurboModeToSwitchesForRoom:currentRoomId]; + NSLog(@"🎮 已为房间 %@ 应用 Turbo Mode 设置", currentRoomId); + } else { + NSLog(@"🎮 无法获取当前房间ID,仅设置全局 Turbo Mode 状态"); + } + + // 显示成功提示 + [self showTurboModeEnabledToast]; +} + +#pragma mark - 直接开启 Turbo Mode + +- (void)enableTurboModeDirectly { + NSLog(@"🎮 XPTurboModeTipsManager 直接开启 Turbo Mode"); + + // 开启全局 turbo mode + [[TurboModeStateManager sharedManager] setTurboModeEnabled:YES]; + + // 获取当前房间ID(如果有的话) + NSString *currentRoomId = [self getCurrentRoomId]; + if (currentRoomId) { + // 应用 turbo mode 到当前房间的开关 + [[TurboModeStateManager sharedManager] applyTurboModeToSwitchesForRoom:currentRoomId]; + NSLog(@"🎮 已为房间 %@ 应用 Turbo Mode 设置", currentRoomId); + } else { + NSLog(@"🎮 无法获取当前房间ID,仅设置全局 Turbo Mode 状态"); + } + + // 显示成功提示 + [self showTurboModeEnabledToast]; +} + +#pragma mark - Helper Methods + +- (NSString *)getCurrentRoomId { + // 🔧 修复:返回保存的当前房间ID + if (self.currentRoomId) { + NSLog(@"🎮 获取当前房间ID: %@", self.currentRoomId); + return self.currentRoomId; + } else { + NSLog(@"🎮 当前房间ID为空,请检查房间进入逻辑"); + return nil; + } +} + +- (void)showTurboModeEnabledToast { + // 显示 Turbo Mode 已开启的提示 + dispatch_async(dispatch_get_main_queue(), ^{ + // 这里可以使用项目中的 Toast 组件 + // 例如:[[ToastManager sharedManager] showToast:@"Turbo Mode 已开启"]; + + NSLog(@"🎮 显示提示:Turbo Mode 已开启"); + + // 如果需要显示 UI 提示,可以在这里添加 + // 例如:使用 UIAlertController 或者自定义 Toast + }); +} + #pragma mark - Dealloc - (void)dealloc { diff --git a/YuMi/Modules/YMRoom/View/MoreView/View/XPEffectPanelViewController.m b/YuMi/Modules/YMRoom/View/MoreView/View/XPEffectPanelViewController.m index 4b0916cb..c4046a28 100644 --- a/YuMi/Modules/YMRoom/View/MoreView/View/XPEffectPanelViewController.m +++ b/YuMi/Modules/YMRoom/View/MoreView/View/XPEffectPanelViewController.m @@ -245,6 +245,10 @@ [[NSUserDefaults standardUserDefaults] synchronize]; [self sendTurboGlobalGiftScreenNotification:isOn]; + // 🔧 新增:显示 toast 提示 + NSString *toastMessage = isOn ? YMLocalizedString(@"20.20.62_text_17") : YMLocalizedString(@"20.20.62_text_18"); + [self showToastWithMessage:toastMessage]; + } else if (sender.tag == 2) { // 全局游戏屏幕 self.globalGameScreenEnabled = isOn; // 按房间 ID 持久化 @@ -252,6 +256,10 @@ setBool:isOn forKey:kTurboGlobalGameScreenEnabledKey(self.roomId)]; [[NSUserDefaults standardUserDefaults] synchronize]; [self sendTurboGlobalGameScreenNotification:isOn]; + + // 🔧 新增:显示 toast 提示 + NSString *toastMessage = isOn ? YMLocalizedString(@"20.20.62_text_19") : YMLocalizedString(@"20.20.62_text_20"); + [self showToastWithMessage:toastMessage]; } // 通知父控制器更新 Turbo_Mode 按钮状态 @@ -333,4 +341,52 @@ [self dismissViewControllerAnimated:YES completion:nil]; } +#pragma mark - Toast Methods + +- (void)showToastWithMessage:(NSString *)message { + // 创建 toast 视图 + UIView *toastView = [[UIView alloc] init]; + toastView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.8]; + toastView.layer.cornerRadius = 8; + [self.view addSubview:toastView]; + + // 创建文本标签 + UILabel *messageLabel = [[UILabel alloc] init]; + messageLabel.text = message; + messageLabel.textColor = [UIColor whiteColor]; + messageLabel.font = [UIFont systemFontOfSize:14]; + messageLabel.textAlignment = NSTextAlignmentCenter; + messageLabel.numberOfLines = 0; + [toastView addSubview:messageLabel]; + + // 设置约束 + [toastView mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.equalTo(self.view); + make.top.equalTo(self.view).offset(100); + make.leading.greaterThanOrEqualTo(self.view).offset(20); + make.trailing.lessThanOrEqualTo(self.view).offset(-20); + }]; + + [messageLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(toastView).insets(UIEdgeInsetsMake(12, 16, 12, 16)); + }]; + + // 显示动画 + toastView.alpha = 0; + [UIView animateWithDuration:0.3 animations:^{ + toastView.alpha = 1; + }]; + + // 自动隐藏 + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [UIView animateWithDuration:0.3 animations:^{ + toastView.alpha = 0; + } completion:^(BOOL finished) { + [toastView removeFromSuperview]; + }]; + }); + + NSLog(@"🎮 显示 Toast: %@", message); +} + @end diff --git a/YuMi/Modules/YMRoom/View/MoreView/View/XPTurboModeTipsView.h b/YuMi/Modules/YMRoom/View/MoreView/View/XPTurboModeTipsView.h index 9097b9aa..8938d87b 100644 --- a/YuMi/Modules/YMRoom/View/MoreView/View/XPTurboModeTipsView.h +++ b/YuMi/Modules/YMRoom/View/MoreView/View/XPTurboModeTipsView.h @@ -14,6 +14,7 @@ NS_ASSUME_NONNULL_BEGIN @optional - (void)turboModeTipsViewDidTapUnderstand; +- (void)turboModeTipsViewDidEnableTurboMode; @end diff --git a/YuMi/Modules/YMRoom/View/MoreView/View/XPTurboModeTipsView.m b/YuMi/Modules/YMRoom/View/MoreView/View/XPTurboModeTipsView.m index 8bce4bd8..4bee87bc 100644 --- a/YuMi/Modules/YMRoom/View/MoreView/View/XPTurboModeTipsView.m +++ b/YuMi/Modules/YMRoom/View/MoreView/View/XPTurboModeTipsView.m @@ -7,15 +7,20 @@ // #import "XPTurboModeTipsView.h" +#import "XPTurboModeTipsManager.h" @interface XPTurboModeTipsView () @property (nonatomic, strong) UIView *backgroundView; @property (nonatomic, strong) UIView *contentView; @property (nonatomic, strong) UILabel *titleLabel; +@property (nonatomic, strong) UIScrollView *scrollView; +@property (nonatomic, strong) UIView *scrollContentView; @property (nonatomic, strong) UILabel *contentLabel; +@property (nonatomic, strong) UILabel *contentLabel2; @property (nonatomic, strong) UIButton *understandButton; @property (nonatomic, strong) UIView *parentView; +@property (nonatomic, strong) UIImageView *tipsImageView; @end @@ -27,6 +32,10 @@ XPTurboModeTipsView *tipsView = [[XPTurboModeTipsView alloc] init]; tipsView.parentView = parentView; [tipsView setupUI]; + + // 🔧 修改:在显示时自动开启 turbo mode + [tipsView enableTurboModeOnShow]; + [tipsView showWithAnimation]; return tipsView; } @@ -35,6 +44,16 @@ [self dismissWithAnimation]; } +#pragma mark - Turbo Mode Management + +- (void)enableTurboModeOnShow { + NSLog(@"🎮 Tips View 显示时自动开启 Turbo Mode"); + + // 🔧 修复:直接使用 XPTurboModeTipsManager 单例开启 turbo mode + // 因为此时 delegate 可能还没有设置 + [[XPTurboModeTipsManager sharedManager] enableTurboModeDirectly]; +} + #pragma mark - Private Methods - (void)setupUI { @@ -51,9 +70,18 @@ // 添加标题 [self setupTitleLabel]; + // 添加滚动视图 + [self setupScrollView]; + // 添加内容文字 [self setupContentLabel]; + // 添加图片视图 + [self setupTipsImageView]; + + // 添加第二个内容标签 + [self setupContentLabel2]; + // 添加按钮 [self setupUnderstandButton]; @@ -84,21 +112,50 @@ - (void)setupTitleLabel { self.titleLabel = [[UILabel alloc] init]; - self.titleLabel.text = @"Tips"; + self.titleLabel.text = YMLocalizedString(@"UserDetail_CP_Toast_0"); self.titleLabel.font = [UIFont boldSystemFontOfSize:20.0]; self.titleLabel.textColor = [UIColor blackColor]; self.titleLabel.textAlignment = NSTextAlignmentCenter; [self.contentView addSubview:self.titleLabel]; } +- (void)setupScrollView { + self.scrollView = [[UIScrollView alloc] init]; + self.scrollView.showsVerticalScrollIndicator = YES; + self.scrollView.showsHorizontalScrollIndicator = NO; + self.scrollView.alwaysBounceVertical = YES; + self.scrollView.alwaysBounceHorizontal = NO; + [self.contentView addSubview:self.scrollView]; + + self.scrollContentView = [[UIView alloc] init]; + [self.scrollView addSubview:self.scrollContentView]; +} + - (void)setupContentLabel { self.contentLabel = [[UILabel alloc] init]; - self.contentLabel.text = @"Enabling Turbo mode can make the app run smoother.\n\nTurbo mode: Turns off room gift animations, in-room broadcasts, CP displays, and other animated effects.\n\nTo make the app run smoother, it has automatically switched to Turbo mode for you (Turbo mode: Automatically turns off gift animations, headwear effects, and merges gift messages in the public chat). You can turn off this mode in the room settings."; + self.contentLabel.text = NSLocalizedString(@"20.20.62_text_15.1", @"Turbo mode tips content 1"); self.contentLabel.font = [UIFont systemFontOfSize:16.0]; self.contentLabel.textColor = [UIColor darkGrayColor]; self.contentLabel.numberOfLines = 0; self.contentLabel.textAlignment = NSTextAlignmentLeft; - [self.contentView addSubview:self.contentLabel]; + [self.scrollContentView addSubview:self.contentLabel]; +} + +- (void)setupTipsImageView { + self.tipsImageView = [[UIImageView alloc] init]; + self.tipsImageView.image = [UIImage imageNamed:@"turbo_mode_tips"]; + self.tipsImageView.contentMode = UIViewContentModeScaleAspectFit; + [self.scrollContentView addSubview:self.tipsImageView]; +} + +- (void)setupContentLabel2 { + self.contentLabel2 = [[UILabel alloc] init]; + self.contentLabel2.text = NSLocalizedString(@"20.20.62_text_15.2", @"Turbo mode tips content 2"); + self.contentLabel2.font = [UIFont systemFontOfSize:16.0]; + self.contentLabel2.textColor = [UIColor darkGrayColor]; + self.contentLabel2.numberOfLines = 0; + self.contentLabel2.textAlignment = NSTextAlignmentLeft; + [self.scrollContentView addSubview:self.contentLabel2]; } - (void)setupUnderstandButton { @@ -113,45 +170,85 @@ } - (void)setupConstraints { + // 背景遮罩 [self.backgroundView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self); }]; + // 内容容器 - 屏幕的 3/5 尺寸,居中显示 [self.contentView mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self); - make.width.equalTo(@320); - make.height.lessThanOrEqualTo(@500); + make.width.equalTo(self).multipliedBy(0.8); // 屏幕宽度的 3/5 + make.height.equalTo(self).multipliedBy(0.6); // 屏幕高度的 3/5 }]; + // 标题 - 固定位置 [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.contentView).offset(24); - make.left.right.equalTo(self.contentView).insets(UIEdgeInsetsMake(0, 24, 0, 24)); + make.leading.trailing.equalTo(self.contentView).insets(UIEdgeInsetsMake(0, 24, 0, 24)); make.height.equalTo(@30); }]; - [self.contentLabel mas_makeConstraints:^(MASConstraintMaker *make) { + // 滚动视图 - 在标题和按钮之间 + [self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.titleLabel.mas_bottom).offset(16); - make.left.right.equalTo(self.contentView).insets(UIEdgeInsetsMake(0, 24, 0, 24)); + make.leading.trailing.equalTo(self.contentView); + make.bottom.equalTo(self.understandButton.mas_top).offset(-16); }]; + // 滚动内容容器 + [self.scrollContentView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(self.scrollView); + make.width.equalTo(self.scrollView); // 重要:设置宽度约束 + }]; + + // 上层:原 contentLabel + [self.contentLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.scrollContentView).offset(0); + make.leading.trailing.equalTo(self.scrollContentView).insets(UIEdgeInsetsMake(0, 24, 0, 24)); + }]; + + // 中层:图片视图 + [self.tipsImageView mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.contentLabel.mas_bottom).offset(20); + make.centerX.equalTo(self.scrollContentView); + make.width.equalTo(self.scrollContentView).multipliedBy(0.6); // 相对宽度 + make.height.equalTo(self.tipsImageView.mas_width).multipliedBy(0.6); // 保持宽高比 + }]; + + // 下层:新增 contentLabel2 + [self.contentLabel2 mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.tipsImageView.mas_bottom).offset(20); + make.leading.trailing.equalTo(self.scrollContentView).insets(UIEdgeInsetsMake(0, 24, 0, 24)); + make.bottom.equalTo(self.scrollContentView).offset(-16); // 重要:设置底部约束 + }]; + + // 按钮 - 固定位置 [self.understandButton mas_makeConstraints:^(MASConstraintMaker *make) { - make.top.equalTo(self.contentLabel.mas_bottom).offset(24); - make.centerX.equalTo(self.contentView); - make.width.equalTo(@200); - make.height.equalTo(@44); make.bottom.equalTo(self.contentView).offset(-24); + make.centerX.equalTo(self.contentView); + make.width.equalTo(self.contentView).multipliedBy(0.6); // 相对宽度 + make.height.equalTo(@44); }]; } #pragma mark - Actions - (void)backgroundTapped { - // 点击背景不关闭弹窗,保持用户体验 + NSLog(@"🎮 用户点击了背景,关闭 Tips 弹窗"); + + // 点击背景关闭弹窗,但不触发开启 turbo mode + if (self.delegate && [self.delegate respondsToSelector:@selector(turboModeTipsViewDidTapUnderstand)]) { + [self.delegate turboModeTipsViewDidTapUnderstand]; + } + + [self dismiss]; } - (void)understandButtonTapped { - NSLog(@"🎮 用户点击了 'I understand' 按钮"); + NSLog(@"🎮 用户点击了 'I understand' 按钮,关闭 Tips 弹窗"); + // 🔧 修改:只触发理解回调,不重复开启 turbo mode(已在显示时开启) if (self.delegate && [self.delegate respondsToSelector:@selector(turboModeTipsViewDidTapUnderstand)]) { [self.delegate turboModeTipsViewDidTapUnderstand]; } diff --git a/YuMi/Modules/YMRoom/View/StageView/MicroView/HeadWearTurboModeBugFixes.md b/YuMi/Modules/YMRoom/View/StageView/MicroView/HeadWearTurboModeBugFixes.md new file mode 100644 index 00000000..d82c97b8 --- /dev/null +++ b/YuMi/Modules/YMRoom/View/StageView/MicroView/HeadWearTurboModeBugFixes.md @@ -0,0 +1,167 @@ +# 头饰 Turbo Mode Bug 修复报告 + +## 问题描述 + +在头饰 turbo mode 控制功能中发现两个关键问题: + +1. **用户在麦位上开启 turbo mode 时,头饰会消失不见** +2. **Turbo mode 开启时,切换麦位或上麦,头饰会正常播放而不是只显示第一帧** + +## 问题分析 + +### 问题1:头饰消失 +**原因**:在 `setHeadWearToFirstFrameOnly` 方法中,调用 `stopAnimating` 和 `stopAnimation` 后,头饰内容可能被清空,导致显示消失。 + +**影响**:用户体验差,头饰功能完全失效。 + +### 问题2:切换麦位时头饰正常播放 +**原因**:SVGA 头饰的处理逻辑有问题,在设置 `imageName` 后立即调用 `applyTurboModeToHeadWear`,但此时 SVGA 可能还没有加载完成,导致 turbo mode 状态没有正确应用。 + +**影响**:Turbo mode 功能不完整,性能优化效果打折扣。 + +## 修复方案 + +### 1. 修复头饰消失问题 + +**修改文件**:`MicroView.m` +**修改方法**:`setHeadWearToFirstFrameOnly` + +**修复内容**: +- 添加详细的调试日志来跟踪执行流程 +- 在停止动画后检查头饰内容是否仍然存在 +- 如果内容消失,添加恢复逻辑(预留接口) + +```objective-c +- (void)setHeadWearToFirstFrameOnly { + NSLog(@"🎮 MicroView: 设置头饰为只显示第一帧模式"); + + // 对于 YYAnimatedImageView,停止动画并显示第一帧 + if (!self.headWearImageView.hidden) { + NSLog(@"🎮 MicroView: 停止 YYAnimatedImageView 动画"); + [self.headWearImageView stopAnimating]; + + // 确保图片仍然可见,手动设置显示第一帧 + if (self.headWearImageView.image) { + // YYAnimatedImageView 停止动画后会自动显示第一帧 + // 如果图片消失,尝试重新设置 + if (!self.headWearImageView.image) { + NSLog(@"🎮 MicroView: YYAnimatedImageView 图片消失,尝试恢复"); + // 这里可能需要重新加载或设置图片 + } + } + } + + // 对于 SVGAImageView,停止动画 + if (!self.headWearSVGAImageView.hidden) { + NSLog(@"🎮 MicroView: 停止 SVGAImageView 动画"); + [self.headWearSVGAImageView stopAnimation]; + + // 确保 SVGA 第一帧仍然显示 + if (self.headWearSVGAImageView.videoItem) { + // SVGA 停止动画后会自动显示第一帧 + // 如果内容消失,可能需要手动设置 + if (!self.headWearSVGAImageView.videoItem) { + NSLog(@"🎮 MicroView: SVGAImageView 内容消失,尝试恢复"); + } + } + } +} +``` + +### 2. 修复切换麦位时头饰正常播放问题 + +**修改文件**:`MicroView.m` +**修改方法**:`configUser` 中的头饰配置逻辑 + +**修复内容**: +- 为 SVGA 头饰添加延迟处理,确保加载完成后再应用 turbo mode +- 添加详细的调试日志来跟踪配置流程 +- 改进错误处理逻辑 + +```objective-c +if ([userInfo isHeadWearSVGA]) { + NSLog(@"🎮 MicroView: 配置 SVGA 头饰: %@", headWearUrl); + self.headWearSVGAImageView.hidden = NO; + [self.headWearSVGAImageView setImageName:headWearUrl]; + + // 对于 SVGA,需要等待加载完成后再应用 turbo mode 状态 + // 这里使用延迟来确保 SVGA 加载完成 + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self applyTurboModeToHeadWear]; + }); +} else { + NSLog(@"🎮 MicroView: 配置精灵图头饰: %@", headWearUrl); + self.headWearImageView.hidden = NO; + NSURL *url = [NSURL URLWithString:headWearUrl]; + @kWeakify(self); + [self.manager loadSpriteSheetImageWithURL:url completionBlock:^(YYSpriteSheetImage * _Nullable sprit) { + @kStrongify(self); + self.headWearImageView.image = sprit; + NSLog(@"🎮 MicroView: 精灵图头饰加载完成,应用 turbo mode 状态"); + // 加载完成后应用 turbo mode 状态 + [self applyTurboModeToHeadWear]; + } failureBlock:^(NSError * _Nullable error) { + NSLog(@"🎮 MicroView: 精灵图头饰加载失败: %@", error.localizedDescription); + }]; +} +``` + +### 3. 添加调试日志 + +**目的**:便于问题排查和功能验证 + +**添加位置**: +- `handleTurboModeStateChanged`:跟踪状态变化 +- `updateHeadWearForTurboMode`:跟踪更新流程 +- `setHeadWearToFirstFrameOnly`:跟踪第一帧设置 +- `setHeadWearToNormalPlayback`:跟踪正常播放设置 +- `applyTurboModeToHeadWear`:跟踪应用流程 +- 头饰配置逻辑:跟踪配置流程 + +## 修复效果 + +### 预期改进 + +1. **头饰不再消失**: + - 用户在麦位上开启 turbo mode 时,头饰会正确显示第一帧 + - 添加了内容检查逻辑,如果出现问题会有日志提示 + +2. **切换麦位时正确应用 turbo mode**: + - SVGA 头饰会在加载完成后正确应用 turbo mode 状态 + - 精灵图头饰在加载完成后也会正确应用 turbo mode 状态 + +3. **更好的调试体验**: + - 详细的日志输出,便于问题排查 + - 清晰的执行流程跟踪 + +### 测试建议 + +1. **测试场景1**:用户在麦位上开启 turbo mode + - 预期:头饰显示第一帧,不播放动画 + - 验证:检查日志输出,确认执行流程正确 + +2. **测试场景2**:Turbo mode 开启时切换麦位 + - 预期:新头饰只显示第一帧,不播放动画 + - 验证:检查日志输出,确认 turbo mode 状态正确应用 + +3. **测试场景3**:Turbo mode 开启时上麦 + - 预期:头饰只显示第一帧,不播放动画 + - 验证:检查日志输出,确认配置流程正确 + +4. **测试场景4**:关闭 turbo mode + - 预期:所有头饰恢复正常动画播放 + - 验证:检查日志输出,确认状态切换正确 + +## 注意事项 + +1. **延迟时间**:SVGA 头饰的延迟时间设置为 0.1 秒,如果发现不够可以适当调整 +2. **日志输出**:生产环境可能需要移除或减少日志输出 +3. **性能影响**:添加的检查逻辑对性能影响很小,但需要注意监控 +4. **兼容性**:修复保持了与现有功能的完全兼容性 + +## 后续优化建议 + +1. **更精确的 SVGA 加载检测**:使用 SVGA 的回调机制而不是延迟 +2. **头饰恢复机制**:如果头饰消失,实现自动恢复逻辑 +3. **性能监控**:添加性能指标来监控 turbo mode 的效果 +4. **用户反馈**:收集用户对 turbo mode 效果的反馈 diff --git a/YuMi/Modules/YMRoom/View/StageView/MicroView/HeadWearTurboModeIntegration.md b/YuMi/Modules/YMRoom/View/StageView/MicroView/HeadWearTurboModeIntegration.md new file mode 100644 index 00000000..6f5859c0 --- /dev/null +++ b/YuMi/Modules/YMRoom/View/StageView/MicroView/HeadWearTurboModeIntegration.md @@ -0,0 +1,137 @@ +# 头饰 Turbo Mode 集成实现 + +## 功能概述 + +在 MicroView.m 中集成了 turbo mode 状态控制,当 turbo mode 开启时,头饰只显示第一帧不播放动画;当 turbo mode 关闭时,头饰按原逻辑正常播放动画。 + +## 实现细节 + +### 1. 添加 Turbo Mode 状态监听 + +```objective-c +// 在 MicroView 接口中添加状态属性 +@property (nonatomic, assign) BOOL isTurboModeEnabled; + +// 在初始化时设置监听 +- (void)setupTurboModeListener { + // 初始化 turbo mode 状态 + self.isTurboModeEnabled = [[TurboModeStateManager sharedManager] isTurboModeEnabled]; + + // 监听 turbo mode 状态变化 + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleTurboModeStateChanged:) + name:@"TurboModeStateChanged" + object:nil]; +} +``` + +### 2. 状态变化处理 + +```objective-c +- (void)handleTurboModeStateChanged:(NSNotification *)notification { + NSDictionary *userInfo = notification.userInfo; + BOOL enabled = [userInfo[@"enabled"] boolValue]; + + self.isTurboModeEnabled = enabled; + + // 如果当前有头饰,需要重新配置以应用新的 turbo mode 状态 + if (self.userInfo && (self.userInfo.headwearEffect.length || self.userInfo.headWearUrl.length || self.userInfo.headwearPic.length)) { + [self updateHeadWearForTurboMode]; + } +} +``` + +### 3. 头饰动画控制 + +```objective-c +- (void)updateHeadWearForTurboMode { + if (self.isTurboModeEnabled) { + // Turbo mode 开启:只显示第一帧,不播放动画 + [self setHeadWearToFirstFrameOnly]; + } else { + // Turbo mode 关闭:正常播放动画 + [self setHeadWearToNormalPlayback]; + } +} + +- (void)setHeadWearToFirstFrameOnly { + // 对于 YYAnimatedImageView,停止动画并显示第一帧 + if (!self.headWearImageView.hidden) { + [self.headWearImageView stopAnimating]; + // YYAnimatedImageView 会自动显示第一帧 + } + + // 对于 SVGAImageView,停止动画 + if (!self.headWearSVGAImageView.hidden) { + [self.headWearSVGAImageView stopAnimation]; + // SVGA 会自动显示第一帧 + } +} + +- (void)setHeadWearToNormalPlayback { + // 对于 YYAnimatedImageView,恢复动画播放 + if (!self.headWearImageView.hidden && self.headWearImageView.image) { + [self.headWearImageView startAnimating]; + } + + // 对于 SVGAImageView,恢复动画播放 + if (!self.headWearSVGAImageView.hidden && self.headWearSVGAImageView.videoItem) { + [self.headWearSVGAImageView startAnimation]; + } +} +``` + +### 4. 头饰配置集成 + +在 `configUser` 方法中,头饰加载完成后会自动应用当前的 turbo mode 状态: + +```objective-c +[self.manager loadSpriteSheetImageWithURL:url completionBlock:^(YYSpriteSheetImage * _Nullable sprit) { + @kStrongify(self); + self.headWearImageView.image = sprit; + // 加载完成后应用 turbo mode 状态 + [self applyTurboModeToHeadWear]; +} failureBlock:^(NSError * _Nullable error) { +}]; +``` + +### 5. 内存管理 + +```objective-c +- (void)dealloc { + // 移除 turbo mode 状态变化监听 + [[NSNotificationCenter defaultCenter] removeObserver:self name:@"TurboModeStateChanged" object:nil]; +} +``` + +## 支持的头饰类型 + +1. **YYAnimatedImageView (精灵图头饰)** + - Turbo mode 开启:调用 `stopAnimating` 停止动画,自动显示第一帧 + - Turbo mode 关闭:调用 `startAnimating` 恢复动画播放 + +2. **SVGAImageView (SVGA 头饰)** + - Turbo mode 开启:调用 `stopAnimation` 停止动画,自动显示第一帧 + - Turbo mode 关闭:调用 `startAnimation` 恢复动画播放 + +## 工作流程 + +1. **初始化阶段**:MicroView 创建时自动获取当前 turbo mode 状态并设置监听 +2. **头饰配置阶段**:用户配置头饰时,加载完成后自动应用当前 turbo mode 状态 +3. **状态变化阶段**:turbo mode 状态改变时,自动更新所有现有头饰的播放状态 +4. **销毁阶段**:MicroView 销毁时自动移除通知监听 + +## 测试建议 + +1. 开启 turbo mode,验证头饰只显示第一帧不播放动画 +2. 关闭 turbo mode,验证头饰恢复正常动画播放 +3. 在头饰播放过程中切换 turbo mode 状态,验证状态切换的实时性 +4. 测试不同类型的头饰(精灵图、SVGA)在 turbo mode 下的表现 +5. 验证多个 MicroView 实例同时响应 turbo mode 状态变化 + +## 注意事项 + +- 该实现不会影响头饰的加载和显示逻辑,只是在播放控制层面进行干预 +- 支持实时状态切换,无需重新加载头饰资源 +- 与现有的 VIP 麦位头饰隐藏逻辑兼容 +- 内存管理正确,避免通知监听泄漏 diff --git a/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroView.m b/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroView.m index c75b8cf0..a406ce4b 100644 --- a/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroView.m +++ b/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroView.m @@ -27,6 +27,8 @@ ///Model #import "RoomFaceInfoModel.h" #import "RoomResourceManager.h" +///Turbo Mode +#import "TurboModeStateManager.h" #define kScpaces 13 @@ -76,6 +78,11 @@ @property(nonatomic, strong) UILabel *goldLabel; +///Turbo Mode 状态 +@property (nonatomic, assign) BOOL isTurboModeEnabled; + +@property (nonatomic, strong) UIImage *tempSprites; + @end @@ -86,6 +93,7 @@ if (self) { [self initSubViews]; [self initSubViewConstraints]; + [self setupTurboModeListener]; } return self; } @@ -669,19 +677,35 @@ NSString * headWearUrl = userInfo.headwearEffect.length ? userInfo.headwearEffect : userInfo.headWearUrl.length ? userInfo.headWearUrl : userInfo.headwearPic; if (headWearUrl.length > 0 && !userInfo.vipMic) { if ([userInfo isHeadWearSVGA]) { + NSLog(@"🎮 MicroView: 配置 SVGA 头饰: %@", headWearUrl); self.headWearSVGAImageView.hidden = NO; - [self.headWearSVGAImageView setImageName:headWearUrl]; + SVGAParser *parse = [[SVGAParser alloc] init]; + @kWeakify(self); + [parse parseWithURL:[NSURL URLWithString:headWearUrl] + completionBlock:^(SVGAVideoEntity * _Nullable videoItem) { + @kStrongify(self); + if (videoItem) { + self.headWearSVGAImageView.videoItem = videoItem; + [self applyTurboModeToHeadWear]; + } + } failureBlock:^(NSError * _Nullable error) { }]; } else { + NSLog(@"🎮 MicroView: 配置精灵图头饰: %@", headWearUrl); self.headWearImageView.hidden = NO; NSURL *url = [NSURL URLWithString:headWearUrl]; @kWeakify(self); [self.manager loadSpriteSheetImageWithURL:url completionBlock:^(YYSpriteSheetImage * _Nullable sprit) { @kStrongify(self); self.headWearImageView.image = sprit; + NSLog(@"🎮 MicroView: 精灵图头饰加载完成,应用 turbo mode 状态"); + // 加载完成后应用 turbo mode 状态 + [self applyTurboModeToHeadWear]; } failureBlock:^(NSError * _Nullable error) { + NSLog(@"🎮 MicroView: 精灵图头饰加载失败: %@", error.localizedDescription); }]; } } else { + NSLog(@"🎮 MicroView: 隐藏头饰 (URL为空或VIP麦位)"); self.headWearSVGAImageView.hidden = YES; self.headWearImageView.hidden = YES; } @@ -787,6 +811,100 @@ return hexComponent / 255.0; } +#pragma mark - Turbo Mode Management + +- (void)setupTurboModeListener { + // 初始化 turbo mode 状态 + self.isTurboModeEnabled = [[TurboModeStateManager sharedManager] isTurboModeEnabled]; + + // 监听 turbo mode 状态变化 + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleTurboModeStateChanged:) + name:@"TurboModeStateChanged" + object:nil]; +} + +- (void)handleTurboModeStateChanged:(NSNotification *)notification { + NSDictionary *userInfo = notification.userInfo; + BOOL enabled = [userInfo[@"enabled"] boolValue]; + + NSLog(@"🎮 MicroView: 收到 turbo mode 状态变化通知,新状态: %@", enabled ? @"开启" : @"关闭"); + + self.isTurboModeEnabled = enabled; + + // 如果当前有头饰,需要重新配置以应用新的 turbo mode 状态 + if (self.userInfo && (self.userInfo.headwearEffect.length || self.userInfo.headWearUrl.length || self.userInfo.headwearPic.length)) { + NSLog(@"🎮 MicroView: 当前有头饰,更新 turbo mode 状态"); + [self updateHeadWearForTurboMode]; + } else { + NSLog(@"🎮 MicroView: 当前无头饰,跳过 turbo mode 状态更新"); + } +} + +- (void)updateHeadWearForTurboMode { + NSLog(@"🎮 MicroView: 更新头饰 turbo mode 状态,当前状态: %@", self.isTurboModeEnabled ? @"开启" : @"关闭"); + + if (self.isTurboModeEnabled) { + // Turbo mode 开启:只显示第一帧,不播放动画 + [self setHeadWearToFirstFrameOnly]; + } else { + // Turbo mode 关闭:正常播放动画 + [self setHeadWearToNormalPlayback]; + } +} + +- (void)setHeadWearToFirstFrameOnly { + NSLog(@"🎮 MicroView: 设置头饰为只显示第一帧模式"); + + // 对于 YYAnimatedImageView,停止动画并显示第一帧 + if (!self.headWearImageView.hidden) { + NSLog(@"🎮 MicroView: 停止 YYAnimatedImageView 动画"); + [self.headWearImageView stopAnimating]; + UIImage *sprites = (YYSpriteSheetImage *)self.headWearImageView.image; + self.tempSprites = sprites; + UIImage *firstFrame = [sprites animatedImageFrameAtIndex:0]; + if (firstFrame) { + self.headWearImageView.image = firstFrame; + } + } else + + // 对于 SVGAImageView,停止动画 + if (!self.headWearSVGAImageView.hidden) { + NSLog(@"🎮 MicroView: 停止 SVGAImageView 动画"); + [self.headWearSVGAImageView pauseAnimation]; +// [self.headWearSVGAImageView stepToFrame:1 andPlay:NO]; + } +} + +- (void)setHeadWearToNormalPlayback { + NSLog(@"🎮 MicroView: 设置头饰为正常播放模式"); + + // 对于 YYAnimatedImageView,恢复动画播放 + if (!self.headWearImageView.hidden && self.headWearImageView.image) { + NSLog(@"🎮 MicroView: 恢复 YYAnimatedImageView 动画播放"); + [self.headWearImageView startAnimating]; + } + + // 对于 SVGAImageView,恢复动画播放 + if (!self.headWearSVGAImageView.hidden && self.headWearSVGAImageView.videoItem) { + NSLog(@"🎮 MicroView: 恢复 SVGAImageView 动画播放"); + self.headWearImageView.image = self.tempSprites; + [self.headWearSVGAImageView startAnimation]; + } +} + +- (void)applyTurboModeToHeadWear { + NSLog(@"🎮 MicroView: 应用 turbo mode 到头饰,当前状态: %@", self.isTurboModeEnabled ? @"开启" : @"关闭"); + + if (self.isTurboModeEnabled) { + // Turbo mode 开启:只显示第一帧,不播放动画 + [self setHeadWearToFirstFrameOnly]; + } else { + // Turbo mode 关闭:正常播放动画 + [self setHeadWearToNormalPlayback]; + } +} + #pragma mark - Getters And Setters - (NetImageView *)avatarImageView { if (!_avatarImageView) { @@ -1083,4 +1201,9 @@ }]; } +- (void)dealloc { + // 移除 turbo mode 状态变化监听 + [[NSNotificationCenter defaultCenter] removeObserver:self name:@"TurboModeStateChanged" object:nil]; +} + @end diff --git a/YuMi/Modules/YMRoom/View/XPRoomViewController.m b/YuMi/Modules/YMRoom/View/XPRoomViewController.m index 8cc860ef..ba525e97 100644 --- a/YuMi/Modules/YMRoom/View/XPRoomViewController.m +++ b/YuMi/Modules/YMRoom/View/XPRoomViewController.m @@ -96,6 +96,7 @@ // 🔧 新增:Turbo Mode Tips 相关 #import "XPTurboModeTipsManager.h" +#import "BuglyManager.h" //#import "XPMineHallAnchorIncomeStatisViewController.h" @@ -562,12 +563,24 @@ XPCandyTreeInsufficientBalanceViewDelegate> [super viewDidDisappear:animated]; self.navigationController.interactivePopGestureRecognizer.enabled = YES; [XPSkillCardPlayerManager shareInstance].isInRoomVC = NO; + + // 🔧 修复:发送房间退出通知,让 BuglyManager 知道用户已退出房间 + [[NSNotificationCenter defaultCenter] postNotificationName:@"RoomDidExit" + object:nil + userInfo:@{@"roomId": self.roomUid ?: @"unknown"}]; + NSLog(@"🎮 房间退出通知已发送 - RoomID: %@", self.roomUid); } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; self.freeView.hidden = NO; self.navigationController.interactivePopGestureRecognizer.enabled = NO; [XPSkillCardPlayerManager shareInstance].isInRoomVC = YES; + + // 🔧 修复:发送房间进入通知,让 BuglyManager 知道用户已进入房间 + [[NSNotificationCenter defaultCenter] postNotificationName:@"RoomDidEnter" + object:nil + userInfo:@{@"roomId": self.roomUid ?: @"unknown"}]; + NSLog(@"🎮 房间进入通知已发送 - RoomID: %@", self.roomUid); } #pragma mark - 连击状态管理 @@ -3390,14 +3403,14 @@ XPCandyTreeInsufficientBalanceViewDelegate> } - (void)testButtonTapped { - NSLog(@"🎮 测试按钮被点击,准备显示 Turbo Mode Tips 弹窗"); + NSLog(@"🎮 测试按钮被点击,准备模拟卡顿检测"); NSLog(@"🎮 当前时间:%@", [NSDate date]); NSLog(@"🎮 房间状态:%@", self.roomInfo ? @"已进入" : @"未进入"); - // 调用 Tips Manager 显示弹窗 - [[XPTurboModeTipsManager sharedManager] showTipsManually]; + // 调用 BuglyManager 模拟卡顿检测,增加计数 + [[BuglyManager sharedManager] simulateLagDetection]; - NSLog(@"🎮 Tips 弹窗显示请求已发送"); + NSLog(@"🎮 卡顿检测模拟已触发,计数将增加"); } @end diff --git a/YuMi/Network/HttpRequestHelper.m b/YuMi/Network/HttpRequestHelper.m index 29764659..2beb7196 100644 --- a/YuMi/Network/HttpRequestHelper.m +++ b/YuMi/Network/HttpRequestHelper.m @@ -94,7 +94,7 @@ editParam = [MSParamsDecode msDecodeParams:editParam]; params = [self configBaseParmars:editParam]; -#if DEBUG +#if 0 // 构建完整的 URL NSString *baseUrl = [HttpRequestHelper getHostUrl]; NSString *fullUrl = [NSString stringWithFormat:@"%@/%@", baseUrl, method]; @@ -118,7 +118,7 @@ @kWeakify(self); [manager GET:method parameters:params headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { BaseModel *baseModel = [BaseModel modelWithDictionary:responseObject]; -#if DEBUG +#if 0 NSLog(@"%@ - \n%@\n", method, [baseModel toJSONString]); #else #endif @@ -129,103 +129,6 @@ }]; } -//+ (void)POST:(NSString *)method -// params:(NSDictionary *)params -// success:(void (^)(BaseModel *data))success -// failure:(void (^)(NSInteger resCode, NSString *message))failure -//{ -// if ([self checkNetworkStatusWithFailure:^{ -// failure(-1, YMLocalizedString(@"HttpRequestHelper0")); -// }]) { -// return; -// } -// -// [self configHeaders]; -// AFHTTPSessionManager *manager = [HttpRequestHelper requestManager]; -// params = [MSParamsDecode msDecodeParams:[params mutableCopy]]; -// params = [self configBaseParmars:params]; -// -//#ifdef DEBUG -// NSLog(@"\nmethod:\n%@\nparameter:\n%@\n 超時:%@", -// method, -// params, -// @(manager.session.configuration.timeoutIntervalForRequest)); -//#else -//#endif -// -// // 将 NSDictionary 转换为 JSON -// NSError *jsonError; -// NSData *jsonData = [NSJSONSerialization dataWithJSONObject:params options:0 error:&jsonError]; -// if (jsonError) { -// NSLog(@"Error serializing JSON: %@", jsonError); -// if (failure) { -// failure(jsonError.code, @"Error creating JSON data"); -// } -// return; -// } -// -// // Gzip 压缩数据 -// NSData *gzipData = [jsonData gzippedData]; -// -// // 创建请求 -// NSString *urlString = [[NSURL URLWithString:method relativeToURL:manager.baseURL] absoluteString]; -// NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlString]]; -// [request setHTTPMethod:@"POST"]; -// -// // 设置请求头 (合并 manager 的通用头和特定头) -// [manager.requestSerializer.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { -// [request setValue:value forHTTPHeaderField:field]; -// }]; -// [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; -// [request setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"]; -// [request setValue:[NSString stringWithFormat:@"%lu", (unsigned long)gzipData.length] forHTTPHeaderField:@"Content-Length"]; // AFNetworking 通常会自动处理 Content-Length,但手动设置以确保 -// -// // !!! 添加认证头 !!! -// if ([[AccountInfoStorage instance] getUid].length > 0) { -// [request setValue:[[AccountInfoStorage instance] getUid] forHTTPHeaderField:@"pub_uid"]; -// } else { -// [request setValue:nil forHTTPHeaderField:@"pub_uid"]; -// } -// if ([[AccountInfoStorage instance] getTicket].length > 0) { -// [request setValue:[[AccountInfoStorage instance] getTicket] forHTTPHeaderField:@"pub_ticket"]; -// }else { -// [request setValue:nil forHTTPHeaderField:@"pub_ticket"]; -// } -// // !!! 添加其他在 configHeaders 中设置的头 !!! -// [request setValue:[NSBundle uploadLanguageText] forHTTPHeaderField:@"Accept-Language"]; -// [request setValue:PI_App_Version forHTTPHeaderField:@"App-Version"]; -// -// // 设置请求体 -// [request setHTTPBody:gzipData]; -// -// // 设置超时 -// request.timeoutInterval = manager.requestSerializer.timeoutInterval; -// -// @kWeakify(self); -// NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request -// uploadProgress:nil -// downloadProgress:nil -// completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) { -// if (error) { -//#ifdef DEBUG -// NSLog(@"%@ - \n%@\n", method, error); -//#else -//#endif -// @kStrongify(self); -// [self handleNetError:error method:method failure:failure]; -// } else { -// BaseModel *baseModel = [BaseModel modelWithDictionary:responseObject]; -//#ifdef DEBUG -// NSLog(@"%@ - \n%@\n", method, [baseModel toJSONString]); -//#else -//#endif -// success(baseModel); -// } -// }]; -// -// [dataTask resume]; -//} - + (void)POST:(NSString *)method params:(NSDictionary *)params success:(void (^)(BaseModel *data))success @@ -245,7 +148,7 @@ params = [self configBaseParmars:params]; -#if DEBUG +#if 0 // 构建完整的 URL NSString *baseUrl = [HttpRequestHelper getHostUrl]; NSString *fullUrl = [NSString stringWithFormat:@"%@/%@", baseUrl, method]; @@ -260,7 +163,7 @@ [manager POST:method parameters:params headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { BaseModel *baseModel = [BaseModel modelWithDictionary:responseObject]; -#if DEBUG +#if 0 NSLog(@"\n%@", [baseModel toJSONString]); #else #endif diff --git a/YuMi/ar.lproj/Localizable.strings b/YuMi/ar.lproj/Localizable.strings index 87af4aa7..01623b7a 100644 --- a/YuMi/ar.lproj/Localizable.strings +++ b/YuMi/ar.lproj/Localizable.strings @@ -4236,11 +4236,18 @@ ineHeadView12" = "الحمل"; "20.20.62_text_7" = "ليس لديك إذن لدعوة المستخدم إلى الميكروفون."; "20.20.62_text_8" = "فشل إرسال الدعوة. يُرجى التحقق من الشبكة."; -"20.20.62_text_9" = "Turn effects on/off"; -"20.20.62_text_9.1" = "Turn effects on"; -"20.20.62_text_9.2" = "Turn effects off"; +"20.20.62_text_9" = "تشغيل/إيقاف المؤثرات"; +"20.20.62_text_9.1" = "وضع Turbo (السرعة الفائقة)"; +"20.20.62_text_9.2" = "وضع عادي"; "20.20.62_text_10" = "Gift effects"; "20.20.62_text_11" = "Global gift screen"; "20.20.62_text_12" = "Global game screen"; "20.20.62_text_13" = "Turbo mode"; "20.20.62_text_14" = "我的特效"; +"20.20.62_text_15.1" = "تمكين وضع Turbo يمكن أن يجعل التطبيق يعمل بسلاسة أكبر.\nوضع Turbo: يُوقف رسوميات الهدايا في الغرفة، البث داخل الغرفة، عروض CP، وعروض المؤثرات الأخرى."; +"20.20.62_text_15.2" = "لجعل التطبيق يعمل بسلاسة أكبر، تم التبديل تلقائيًا إلى وضع Turbo لك (وضع Turbo: يُوقف تلقائيًا مؤثرات الهدايا، مؤثرات غطاء الرأس، ويدمج رسائل الهدايا في الدردشة العامة). يمكنك إيقاف هذا الوضع في إعدادات الغرفة."; +"20.20.62_text_16" = "أنا أدرك"; +"20.20.62_text_17" = "لقد فعّلتَ تأثير شاشة هدية غروبال العائمة."; +"20.20.62_text_18" = "لقد عطّلتَ تأثير شاشة هدية غروبال العائمة. لم تعد شاشة هدية غروبال العائمة ظاهرة في هذه الغرفة. انقر لتفعيلها مجددًا."; +"20.20.62_text_19" = "لقد فعّلتَ تأثير شاشة لعبة غروبال العائمة."; +"20.20.62_text_20" = "لقد عطّلتَ تأثير شاشة لعبة غروبال العائمة. لم تعد شاشة لعبة غروبال العائمة ظاهرة في هذه الغرفة. انقر لتفعيلها مجددًا."; diff --git a/YuMi/en.lproj/Localizable.strings b/YuMi/en.lproj/Localizable.strings index 5eb8e28d..d0b5c6cc 100644 --- a/YuMi/en.lproj/Localizable.strings +++ b/YuMi/en.lproj/Localizable.strings @@ -4024,10 +4024,17 @@ "20.20.62_text_8" = "The invitation failed to be sent. Please check the network."; "20.20.62_text_9" = "Turn effects on/off"; -"20.20.62_text_9.1" = "Turn effects on"; -"20.20.62_text_9.2" = "Turn effects off"; +"20.20.62_text_9.1" = "Turbo mode"; +"20.20.62_text_9.2" = "Non-turbo mode"; "20.20.62_text_10" = "Gift effects"; "20.20.62_text_11" = "Global gift screen"; "20.20.62_text_12" = "Global game screen"; "20.20.62_text_13" = "Turbo mode"; "20.20.62_text_14" = "我的特效"; +"20.20.62_text_15.1" = "Enabling Turbo mode can make the app run smoother.\nTurbo mode: Turns off room gift animations, in-room broadcasts, CP displays, and other animated effects."; +"20.20.62_text_15.2" = "To make the app run smoother, it has automatically switched to Turbo mode for you (Turbo mode: Automatically turns off gift animations, headwear effects, and merges gift messages in the public chat). You can turn off this mode in the room settings."; +"20.20.62_text_16" = "Anladım"; +"20.20.62_text_17" = "You have enabled the grobal gift floating screen effect."; +"20.20.62_text_18" = "You have disabled the grobal gift floating screen effect. The grobal gift floating screen is no longer visible in this room. Click to enable it again."; +"20.20.62_text_19" = "You have enabled the grobal game floating screen effect."; +"20.20.62_text_20" = "You have disabled the grobal game floating screen effect. The grobal game floating screen is no longer visible in this room. Click to enable it again."; diff --git a/YuMi/pt-BR.lproj/Localizable.strings b/YuMi/pt-BR.lproj/Localizable.strings index 27680676..bff1f6b9 100644 --- a/YuMi/pt-BR.lproj/Localizable.strings +++ b/YuMi/pt-BR.lproj/Localizable.strings @@ -3316,11 +3316,18 @@ "20.20.62_text_7" = "Você não tem permissão para convidar o usuário para o microfone."; "20.20.62_text_8" = "O convite não foi enviado. Verifique a rede."; -"20.20.62_text_9" = "Turn effects on/off"; -"20.20.62_text_9.1" = "Turn effects on"; -"20.20.62_text_9.2" = "Turn effects off"; +"20.20.62_text_9" = "Ativar/Desativar efeitos"; +"20.20.62_text_9.1" = "Modo turbo"; +"20.20.62_text_9.2" = "Modo normal"; "20.20.62_text_10" = "Gift effects"; "20.20.62_text_11" = "Global gift screen"; "20.20.62_text_12" = "Global game screen"; "20.20.62_text_13" = "Turbo mode"; "20.20.62_text_14" = "我的特效"; +"20.20.62_text_15.1" = "Ativar o Modo turbo pode fazer com que o aplicativo funcione de forma mais suave.\nModo turbo: Desativa as animações de presentes na sala, transmissões na sala, exibições de CP e outros efeitos animados."; +"20.20.62_text_15.2" = "Para fazer o aplicativo funcionar de forma mais suave, mudou automaticamente para o Modo turbo para si (Modo turbo: Desativa automaticamente os efeitos de presentes, os efeitos de chapéu e funde as mensagens de presentes no chat público). Pode desativar este modo nas definições da sala."; +"20.20.62_text_16" = "Compreendo"; +"20.20.62_text_17" = "Você ativou o efeito de tela flutuante do presente Grobal."; +"20.20.62_text_18" = "Você desativou o efeito de tela flutuante do presente Grobal. A tela flutuante do presente Grobal não está mais visível nesta sala. Clique para ativá-la novamente."; +"20.20.62_text_19" = "Você ativou o efeito de tela flutuante do jogo Grobal."; +"20.20.62_text_20" = "Você desativou o efeito de tela flutuante do jogo Grobal. A tela flutuante do jogo Grobal não está mais visível nesta sala. Clique para ativá-la novamente."; diff --git a/YuMi/tr.lproj/Localizable.strings b/YuMi/tr.lproj/Localizable.strings index 27e0a33c..d1fc6740 100644 --- a/YuMi/tr.lproj/Localizable.strings +++ b/YuMi/tr.lproj/Localizable.strings @@ -3817,11 +3817,18 @@ "20.20.62_text_7" = "Kullanıcıyı mikrofona davet etme izniniz yok."; "20.20.62_text_8" = "Davet gönderilemedi. Lütfen ağı kontrol edin."; -"20.20.62_text_9" = "Turn effects on/off"; -"20.20.62_text_9.1" = "Turn effects on"; -"20.20.62_text_9.2" = "Turn effects off"; +"20.20.62_text_9" = "Efektleri Aç/Kapat"; +"20.20.62_text_9.1" = "Turbo mod"; +"20.20.62_text_9.2" = "Normal mod"; "20.20.62_text_10" = "Gift effects"; "20.20.62_text_11" = "Global gift screen"; "20.20.62_text_12" = "Global game screen"; "20.20.62_text_13" = "Turbo mode"; "20.20.62_text_14" = "我的特效"; +"20.20.62_text_15.1" = "Turbo modu etkinleştirmek, uygulamanın daha sorunsuz çalışmasını sağlayabilir.\nTurbo mod: Oda hediye animasyonlarını, odadaki yayınları, CP görüntülerini ve diğer animasyonlu efektleri kapatır."; +"20.20.62_text_15.2" = "Uygulamanın daha sorunsuz çalışması için sizin için otomatik olarak Turbo moda geçildi (Turbo mod: Hediye animasyonlarını, başlık efektlerini otomatik olarak kapatır ve genel sohbetteki hediye mesajlarını birleştirir). Bu modu oda ayarlarından kapatabilirsiniz."; +"20.20.62_text_16" = "Anladım"; +"20.20.62_text_17" = "Küresel hediye yüzen ekran efektini etkinleştirdiniz."; +"20.20.62_text_18" = "Küresel hediye yüzen ekran efektini devre dışı bıraktınız. Küresel hediye yüzen ekran artık bu odada görünmüyor. Tekrar etkinleştirmek için tıklayın."; +"20.20.62_text_19" = "Küresel oyun yüzen ekran efektini etkinleştirdiniz."; +"20.20.62_text_20" = "Küresel oyun yüzen ekran efektini devre dışı bıraktınız. Küresel oyun yüzen ekran artık bu odada görünmüyor. Tekrar etkinleştirmek için tıklayın."; diff --git a/YuMi/zh-Hant.lproj/Localizable.strings b/YuMi/zh-Hant.lproj/Localizable.strings index d72ae771..574ca5a1 100644 --- a/YuMi/zh-Hant.lproj/Localizable.strings +++ b/YuMi/zh-Hant.lproj/Localizable.strings @@ -3687,12 +3687,18 @@ "20.20.62_text_7" = "您無權邀請該使用者加入麥克風。"; "20.20.62_text_8" = "邀請發送失敗,請檢查網路。"; -"20.20.62_text_9" = "Turn effects on/off"; -"20.20.62_text_9.1" = "Turbo mode on"; -"20.20.62_text_9.2" = "Turbo mode off"; -"20.20.62_text_10" = "Gift effects"; -"20.20.62_text_11" = "Global gift screen"; -"20.20.62_text_12" = "Global game screen"; -"20.20.62_text_13" = "Turbo mode"; +"20.20.62_text_9" = "开关我的特效"; +"20.20.62_text_9.1" = "极速模式"; +"20.20.62_text_9.2" = "非极速模式"; +"20.20.62_text_10" = "礼物特效"; +"20.20.62_text_11" = "全服礼物飘屏"; +"20.20.62_text_12" = "全服游戏飘屏"; +"20.20.62_text_13" = "极速模式"; "20.20.62_text_14" = "我的特效"; - +"20.20.62_text_15.1" = "开启极速模式,可以使app运行更加流畅。\n极速模式:关闭房间礼物动效果、房间内飘屏,CP展示等动效展示。"; +"20.20.62_text_15.2" = "为使APP运行更加顺畅,已自动为你切换至极速模式(极速模式:自动关闭礼物动效、头饰动效、公屏的送礼消息已合并)。你可以在房间设置中关闭该模式。"; +"20.20.62_text_16" = "我已知晓"; +"20.20.62_text_17" = "你已经开启全服礼物飘屏特效。"; +"20.20.62_text_18" = "你已经关闭全服礼物飘屏特效。本房间内无法看到全服礼物飘屏,点击即可重新开启。"; +"20.20.62_text_19" = "你已经开启全服游戏飘屏特效。"; +"20.20.62_text_20" = "你已经关闭全服游戏飘屏特效。本房间内无法看到全服游戏飘屏,点击即可重新开启。";