From 024936570cc2f7abbd11b69e0ba3640076dd529f Mon Sep 17 00:00:00 2001 From: edwinQQQ Date: Fri, 19 Sep 2025 23:17:56 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=9F=B3=E9=87=8F=E8=AF=84?= =?UTF-8?q?=E4=BC=B0=E5=8F=82=E6=95=B0=EF=BC=8C=E5=B0=86=E6=9C=80=E5=A4=A7?= =?UTF-8?q?=E8=AF=84=E4=BC=B0=E6=97=B6=E9=97=B4=E4=BB=8E900=E6=AF=AB?= =?UTF-8?q?=E7=A7=92=E6=9B=B4=E6=94=B9=E4=B8=BA300=E6=AF=AB=E7=A7=92?= =?UTF-8?q?=EF=BC=9B=E5=9C=A8=E5=A4=84=E7=90=86=E5=85=AC=E5=85=B1=E6=88=BF?= =?UTF-8?q?=E9=97=B4=E6=B6=88=E6=81=AF=E6=97=B6=EF=BC=8C=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=AF=B9=20NIM=20=E9=80=9A=E7=9F=A5=E5=AF=B9=E8=B1=A1=E7=9A=84?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E6=A3=80=E6=9F=A5=EF=BC=8C=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E5=9C=A8=E7=89=B9=E5=AE=9A=E4=BA=8B=E4=BB=B6=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=B8=8B=E4=B8=8D=E8=BF=9B=E8=A1=8C=E5=A4=84=E7=90=86=EF=BC=9B?= =?UTF-8?q?=E6=89=A9=E5=B1=95=E7=A4=BC=E7=89=A9=E5=92=8C=E6=B8=B8=E6=88=8F?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E7=9A=84=E6=A8=AA=E5=B9=85=E6=B8=85=E7=90=86?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E7=A1=AE=E4=BF=9D=E5=9C=A8=E7=89=B9?= =?UTF-8?q?=E5=AE=9A=E6=9D=A1=E4=BB=B6=E4=B8=8B=E6=AD=A3=E7=A1=AE=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E6=A8=AA=E5=B9=85=EF=BC=9B=E6=96=B0=E5=A2=9E=E6=B8=85?= =?UTF-8?q?=E7=90=86=20CP=20=E7=9B=B8=E5=85=B3=E6=A8=AA=E5=B9=85=E7=9A=84?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E7=A1=AE=E4=BF=9D=E5=9C=A8=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E7=A4=BC=E7=89=A9=E7=89=B9=E6=95=88=E6=97=B6=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E5=B1=95=E7=A4=BA=EF=BC=9B=E5=AE=9E=E7=8E=B0=E9=BA=A6?= =?UTF-8?q?=E5=85=8B=E9=A3=8E=E9=98=9F=E5=88=97=E7=9A=84=E6=B7=B1=E5=BA=A6?= =?UTF-8?q?=E5=A4=8D=E5=88=B6=EF=BC=8C=E7=A1=AE=E4=BF=9D=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E4=B8=80=E8=87=B4=E6=80=A7=EF=BC=9B=E9=87=8D=E7=BD=AE=E5=A3=B0?= =?UTF-8?q?=E6=B3=A2=E5=8A=A8=E7=94=BB=E7=8A=B6=E6=80=81=E4=BB=A5=E9=81=BF?= =?UTF-8?q?=E5=85=8D=E8=A7=86=E5=9B=BE=E5=A4=8D=E7=94=A8=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- YuMi/Modules/YMRTC/RtcImpl/TRTCRtcImpl.m | 2 +- .../View/AnimationView/RoomAnimationView.m | 125 ++++++++++++- .../StageView/MicroView/MicroNobleWaveView.h | 2 + .../View/StageView/MicroView/MicroView.m | 6 + .../View/StageView/MicroView/MicroWaveView.m | 5 +- .../Modules/YMRoom/View/StageView/StageView.m | 174 +++++++++++++++++- .../YMRoom/View/XPRoomViewController.m | 12 +- 7 files changed, 303 insertions(+), 23 deletions(-) diff --git a/YuMi/Modules/YMRTC/RtcImpl/TRTCRtcImpl.m b/YuMi/Modules/YMRTC/RtcImpl/TRTCRtcImpl.m index 49b314f5..6985de2b 100644 --- a/YuMi/Modules/YMRTC/RtcImpl/TRTCRtcImpl.m +++ b/YuMi/Modules/YMRTC/RtcImpl/TRTCRtcImpl.m @@ -33,7 +33,7 @@ AudioVolumeParams.enableVadDetection = YES; AudioVolumeParams.enablePitchCalculation = YES; AudioVolumeParams.enableSpectrumCalculation = YES; - [self.engine enableAudioVolumeEvaluation:900 withParams:AudioVolumeParams]; + [self.engine enableAudioVolumeEvaluation:300 withParams:AudioVolumeParams]; [TRTCCloud setConsoleEnabled:NO]; [_engine addDelegate:self]; } diff --git a/YuMi/Modules/YMRoom/View/AnimationView/RoomAnimationView.m b/YuMi/Modules/YMRoom/View/AnimationView/RoomAnimationView.m index 0013c0fb..a12e0e2a 100644 --- a/YuMi/Modules/YMRoom/View/AnimationView/RoomAnimationView.m +++ b/YuMi/Modules/YMRoom/View/AnimationView/RoomAnimationView.m @@ -741,7 +741,11 @@ BannerSchedulerDelegate } } else if (obj.second == Custom_Message_Sub_Super_Gift_Banner || obj.second == Custom_Message_Sub_Gift_ChannelNotify || - obj.second == Custom_Message_Sub_LuckyPackage) { + obj.second == Custom_Message_Sub_LuckyPackage || + obj.second == Custom_Message_Sub_Super_Gift_Winning_Coins_ALL_Room || + obj.second == Custom_Message_Sub_Room_Gift_LuckBag || + obj.second == Custom_Message_Sub_Room_Gift_LuckBag_Server || + obj.second == Custom_Message_Sub_Room_Gift_LuckBag_FullScree) { // 礼物相关banner BOOL allowGiftScreen = [[TurboModeStateManager sharedManager] isGlobalGiftScreenEnabledForRoom:roomIdForTurbo]; if (!allowGiftScreen) { @@ -3900,7 +3904,8 @@ BannerSchedulerDelegate for (UIView *subview in self.bannerContainer.subviews) { if ([subview isKindOfClass:[BravoGiftBannerView class]] || [subview isKindOfClass:[LuckyGiftWinningBannerView class]] || - [subview isKindOfClass:[RoomHighValueGiftBannerAnimation class]]) { + [subview isKindOfClass:[RoomHighValueGiftBannerAnimation class]] || + [subview isKindOfClass:[LuckyPackageBannerView class]]) { [viewsToRemove addObject:subview]; } } @@ -3909,6 +3914,22 @@ BannerSchedulerDelegate [view removeFromSuperview]; } + // 清空队列中礼物相关的banner(包括:超级礼物banner、礼物频道通知、福袋banner) + if (self.bannerScheduler) { + // 逆序移除以避免索引移动问题 + for (NSInteger i = self.bannerScheduler.queueCount - 1; i >= 0; i--) { + id queued = [self.bannerScheduler bannerAtIndex:i]; + if ([queued isKindOfClass:[AttachmentModel class]]) { + AttachmentModel *att = (AttachmentModel *)queued; + if (att.second == Custom_Message_Sub_Super_Gift_Banner || + att.second == Custom_Message_Sub_Gift_ChannelNotify || + att.second == Custom_Message_Sub_LuckyPackage) { + [self.bannerScheduler removeBannerAtIndex:i]; + } + } + } + } + // 标记banner播放完成,继续下一个 if (self.isRoomBannerV2Displaying) { self.isRoomBannerV2Displaying = NO; @@ -3934,6 +3955,20 @@ BannerSchedulerDelegate [view removeFromSuperview]; } + // 清空队列中游戏相关的banner(通用飘屏:单房/全房) + if (self.bannerScheduler) { + for (NSInteger i = self.bannerScheduler.queueCount - 1; i >= 0; i--) { + id queued = [self.bannerScheduler bannerAtIndex:i]; + if ([queued isKindOfClass:[AttachmentModel class]]) { + AttachmentModel *att = (AttachmentModel *)queued; + if (att.second == Custom_Message_Sub_General_Floating_Screen_One_Room || + att.second == Custom_Message_Sub_General_Floating_Screen_All_Room) { + [self.bannerScheduler removeBannerAtIndex:i]; + } + } + } + } + // 标记banner播放完成,继续下一个 if (self.isRoomBannerV2Displaying) { self.isRoomBannerV2Displaying = NO; @@ -3977,14 +4012,38 @@ BannerSchedulerDelegate #pragma mark - BannerSchedulerDelegate - (void)bannerScheduler:(BannerScheduler *)scheduler shouldPlayBanner:(id)banner { - - // 将 Banner 数据转换为 AttachmentModel 并播放 - if ([banner isKindOfClass:[AttachmentModel class]]) { - AttachmentModel *attachment = (AttachmentModel *)banner; - [self _playBannerWithAttachment:attachment]; - } else { + // 将 Banner 数据转换为 AttachmentModel + if (![banner isKindOfClass:[AttachmentModel class]]) { NSLog(@"⚠️ BannerSchedulerDelegate: Banner 不是 AttachmentModel 类型"); + [self.bannerScheduler markBannerFinished]; + return; } + AttachmentModel *attachment = (AttachmentModel *)banner; + + // 二次校验:出队播放前按 TurboMode 再过滤一次,防止进房竞态导致漏过滤 + NSString *roomIdForTurbo = self.currentRoomId ?: @([self.hostDelegate getRoomInfo].roomId).stringValue; + BOOL allowed = YES; + + if (attachment.second == Custom_Message_Sub_General_Floating_Screen_One_Room || + attachment.second == Custom_Message_Sub_General_Floating_Screen_All_Room) { + allowed = [[TurboModeStateManager sharedManager] isGlobalGameScreenEnabledForRoom:roomIdForTurbo]; + } else if (attachment.second == Custom_Message_Sub_Super_Gift_Banner || + attachment.second == Custom_Message_Sub_Gift_ChannelNotify || + attachment.second == Custom_Message_Sub_LuckyPackage) { + allowed = [[TurboModeStateManager sharedManager] isGlobalGiftScreenEnabledForRoom:roomIdForTurbo]; + } else if (attachment.second == Custom_Message_Sub_CP_Gift || + attachment.second == Custom_Message_Sub_CP_Upgrade || + attachment.second == Custom_Message_Sub_CP_Binding) { + allowed = [[TurboModeStateManager sharedManager] isGiftEffectsEnabledForRoom:roomIdForTurbo]; + } + + if (!allowed) { + NSLog(@"🎮 TurboMode 二次校验拒绝播放 banner(second=%ld) - 房间ID:%@", (long)attachment.second, roomIdForTurbo); + [self.bannerScheduler markBannerFinished]; + return; + } + + [self _playBannerWithAttachment:attachment]; } - (void)bannerSchedulerDidFinishPlaying:(BannerScheduler *)scheduler { @@ -4188,6 +4247,12 @@ BannerSchedulerDelegate // 如果关闭,清理当前效果 if (!enabled) { [self cleanupGiftEffects]; + // 同时清理 CP 类 Banner(礼物特效开关关闭时不应再展示 CP 相关) + if (self.currentRoomId.length > 0) { + [self cleanupCpBanners]; + } else { + [self cleanupCpBanners]; + } } NSLog(@"🎮 RoomAnimationView 礼物特效状态变化: %@", enabled ? @"开启" : @"关闭"); @@ -4225,6 +4290,50 @@ BannerSchedulerDelegate NSLog(@"🎮 RoomAnimationView CP麦位开关状态变化: %@", enabled ? @"开启" : @"关闭"); } +// 🔧 新增:清理 CP 相关 banner(受礼物特效开关影响) +- (void)cleanupCpBanners { + NSLog(@"🎮 清理 CP 相关banner"); + + // 清理可见的 CP 相关视图 + NSMutableArray *viewsToRemove = [NSMutableArray array]; + for (UIView *subview in self.bannerContainer.subviews) { + if ([subview isKindOfClass:[CPGiftBanner class]]) { + [viewsToRemove addObject:subview]; + } + } + for (UIView *subview in self.topContainer.subviews) { + if ([subview isKindOfClass:[CPBindingAnimation class]] || + [subview isKindOfClass:[CPLevelUpAnimation class]]) { + [viewsToRemove addObject:subview]; + } + } + for (UIView *v in viewsToRemove) { + [v removeFromSuperview]; + } + + // 清空队列中 CP 相关的 banner(CP 礼物/升级/绑定) + if (self.bannerScheduler) { + for (NSInteger i = self.bannerScheduler.queueCount - 1; i >= 0; i--) { + id queued = [self.bannerScheduler bannerAtIndex:i]; + if ([queued isKindOfClass:[AttachmentModel class]]) { + AttachmentModel *att = (AttachmentModel *)queued; + if (att.second == Custom_Message_Sub_CP_Gift || + att.second == Custom_Message_Sub_CP_Upgrade || + att.second == Custom_Message_Sub_CP_Binding) { + [self.bannerScheduler removeBannerAtIndex:i]; + } + } + } + } + + if (self.isRoomBannerV2Displaying) { + self.isRoomBannerV2Displaying = NO; + [self.bannerScheduler markBannerFinished]; + } + + NSLog(@"🎮 CP 相关banner清理完成"); +} + // 🔧 新增:处理当前房间ID设置通知 - (void)handleCurrentRoomIdSet:(NSNotification *)notification { NSString *roomId = notification.userInfo[@"roomId"]; diff --git a/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroNobleWaveView.h b/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroNobleWaveView.h index 9f749f6f..69c2aa86 100644 --- a/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroNobleWaveView.h +++ b/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroNobleWaveView.h @@ -14,6 +14,8 @@ NS_ASSUME_NONNULL_BEGIN ///开始展示声波动画 - (void)startWaveAnimationWithUrl:(NSString *)url; - (void)startWaveAnimationWithVideoItem:(SVGAVideoEntity *)videoItem; +///结束声波动画 +- (void)stopWaveAnimation; @end diff --git a/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroView.m b/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroView.m index 7f5188ab..a59b3424 100644 --- a/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroView.m +++ b/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroView.m @@ -584,6 +584,9 @@ self.microModel = model; MicroStateModel * micStats = model.microState; UserInfoModel * userInfo = model.userInfo; + // 重置声波/贵族光圈动画状态,避免复用视图在多次切换麦位后处于不可见/不可重启状态 + [self.animationView stopWaveAnimation]; + [self.nobleWaveView stopWaveAnimation]; [self configMicroState:micStats]; [self configUser:userInfo]; } @@ -638,6 +641,9 @@ - (void)configUser:(UserInfoModel *)userInfo { self.userInfo = userInfo; + // 绑定新用户时重置一次动画,确保后续 userSpeaking 立即可启动 + [self.animationView stopWaveAnimation]; + [self.nobleWaveView stopWaveAnimation]; self.nickLabel.hidden = NO; self.sexImageView.hidden = YES; diff --git a/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroWaveView.m b/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroWaveView.m index 6caec30a..c74d7e21 100644 --- a/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroWaveView.m +++ b/YuMi/Modules/YMRoom/View/StageView/MicroView/MicroWaveView.m @@ -42,9 +42,8 @@ return; } - if (self.isAnimationing) { - [self.firstView.layer removeAllAnimations]; - } + [self.firstView.layer removeAllAnimations]; + self.isAnimationing = YES; self.firstView.hidden = NO; [self animationWithLayer:self.firstView]; diff --git a/YuMi/Modules/YMRoom/View/StageView/StageView.m b/YuMi/Modules/YMRoom/View/StageView/StageView.m index 3a7f5b15..3005ffe5 100644 --- a/YuMi/Modules/YMRoom/View/StageView/StageView.m +++ b/YuMi/Modules/YMRoom/View/StageView/StageView.m @@ -281,6 +281,7 @@ [RtcManager instance].localMuted = YES; } } + if (roomInfo.type == RoomType_Anchor && [[AccountInfoStorage instance].getUid isEqualToString:[NSString stringWithFormat:@"%ld", roomInfo.uid]]) { selfNeedBroadcast = YES;///个播房房主默认角色为主播 } @@ -290,8 +291,7 @@ [statisMicArray addObject:str]; } } - if (statisMicArray.count) { - } + if (leaveMode) { UserInfoModel *owner = [[UserInfoModel alloc] init]; owner.avatar = anchorModel.userInfo.avatar? anchorModel.userInfo.avatar : roomInfo.avatar; @@ -428,7 +428,142 @@ } - (NSMutableDictionary *)getMicroQueue { - return self.micQueue; + // 创建 micQueue 的深度副本,确保所有内部对象都是副本 + NSMutableDictionary *deepCopy = [NSMutableDictionary dictionary]; + + for (NSString *position in self.micQueue.allKeys) { + MicroQueueModel *originalModel = [self.micQueue objectForKey:position]; + MicroQueueModel *copiedModel = [[MicroQueueModel alloc] init]; + + // 复制 microState + if (originalModel.microState) { + MicroStateModel *copiedMicroState = [[MicroStateModel alloc] init]; + copiedMicroState.position = originalModel.microState.position; + copiedMicroState.posState = originalModel.microState.posState; + copiedMicroState.micState = originalModel.microState.micState; + copiedMicroState.datingMicType = originalModel.microState.datingMicType; + copiedMicroState.indexOffset = originalModel.microState.indexOffset; + copiedModel.microState = copiedMicroState; + } + + // 复制 userInfo(深度复制) + if (originalModel.userInfo) { + UserInfoModel *copiedUserInfo = [[UserInfoModel alloc] init]; + // 复制所有基本属性 + copiedUserInfo.bindType = originalModel.userInfo.bindType; + copiedUserInfo.createTime = originalModel.userInfo.createTime; + copiedUserInfo.parentMode = originalModel.userInfo.parentMode; + copiedUserInfo.isBindPhone = originalModel.userInfo.isBindPhone; + copiedUserInfo.erbanNo = originalModel.userInfo.erbanNo; + copiedUserInfo.registerDay = originalModel.userInfo.registerDay; + copiedUserInfo.isFirstCharge = originalModel.userInfo.isFirstCharge; + copiedUserInfo.hasPrettyErbanNo = originalModel.userInfo.hasPrettyErbanNo; + copiedUserInfo.isBindApple = originalModel.userInfo.isBindApple; + copiedUserInfo.fansNum = originalModel.userInfo.fansNum; + copiedUserInfo.isBindBankCard = originalModel.userInfo.isBindBankCard; + copiedUserInfo.hasRegPacket = originalModel.userInfo.hasRegPacket; + copiedUserInfo.gender = originalModel.userInfo.gender; + copiedUserInfo.platformRole = originalModel.userInfo.platformRole; + copiedUserInfo.uid = originalModel.userInfo.uid; + copiedUserInfo.defUser = originalModel.userInfo.defUser; + copiedUserInfo.remainDay = originalModel.userInfo.remainDay; + copiedUserInfo.isReview = originalModel.userInfo.isReview; + copiedUserInfo.newUser = originalModel.userInfo.newUser; + copiedUserInfo.followNum = originalModel.userInfo.followNum; + copiedUserInfo.isBindPaymentPwd = originalModel.userInfo.isBindPaymentPwd; + copiedUserInfo.isBindXCZAccount = originalModel.userInfo.isBindXCZAccount; + copiedUserInfo.isBindAlipay = originalModel.userInfo.isBindAlipay; + copiedUserInfo.isBindPasswd = originalModel.userInfo.isBindPasswd; + copiedUserInfo.visitNum = originalModel.userInfo.visitNum; + copiedUserInfo.inRoomNum = originalModel.userInfo.inRoomNum; + copiedUserInfo.showLimitCharge = originalModel.userInfo.showLimitCharge; + copiedUserInfo.limitChargeEndTime = originalModel.userInfo.limitChargeEndTime; + copiedUserInfo.isCertified = originalModel.userInfo.isCertified; + copiedUserInfo.isCustomWord = originalModel.userInfo.isCustomWord; + copiedUserInfo.otherViewType = originalModel.userInfo.otherViewType; + copiedUserInfo.headwearType = originalModel.userInfo.headwearType; + copiedUserInfo.headWearType = originalModel.userInfo.headWearType; + copiedUserInfo.vipMic = originalModel.userInfo.vipMic; + copiedUserInfo.hasSelectUser = originalModel.userInfo.hasSelectUser; + copiedUserInfo.selectMicPosition = originalModel.userInfo.selectMicPosition; + copiedUserInfo.gameStatus = originalModel.userInfo.gameStatus; + copiedUserInfo.groupType = originalModel.userInfo.groupType; + copiedUserInfo.preventKick = originalModel.userInfo.preventKick; + copiedUserInfo.fromSayHelloChannel = originalModel.userInfo.fromSayHelloChannel; + copiedUserInfo.banAccount = originalModel.userInfo.banAccount; + copiedUserInfo.isRechargeUser = originalModel.userInfo.isRechargeUser; + copiedUserInfo.isNoProhibitMic = originalModel.userInfo.isNoProhibitMic; + copiedUserInfo.hasSuperRole = originalModel.userInfo.hasSuperRole; + copiedUserInfo.uploadGifAvatarPrice = originalModel.userInfo.uploadGifAvatarPrice; + copiedUserInfo.birth = originalModel.userInfo.birth; + + // 复制字符串属性 + copiedUserInfo.phone = [originalModel.userInfo.phone copy]; + copiedUserInfo.email = [originalModel.userInfo.email copy]; + copiedUserInfo.nick = [originalModel.userInfo.nick copy]; + copiedUserInfo.avatar = [originalModel.userInfo.avatar copy]; + copiedUserInfo.reviewingAvatar = [originalModel.userInfo.reviewingAvatar copy]; + copiedUserInfo.region = [originalModel.userInfo.region copy]; + copiedUserInfo.userDesc = [originalModel.userInfo.userDesc copy]; + copiedUserInfo.nameplatePic = [originalModel.userInfo.nameplatePic copy]; + copiedUserInfo.nameplateWord = [originalModel.userInfo.nameplateWord copy]; + copiedUserInfo.roomUid = [originalModel.userInfo.roomUid copy]; + copiedUserInfo.roomTitle = [originalModel.userInfo.roomTitle copy]; + copiedUserInfo.carEffect = [originalModel.userInfo.carEffect copy]; + copiedUserInfo.viewUrl = [originalModel.userInfo.viewUrl copy]; + copiedUserInfo.carName = [originalModel.userInfo.carName copy]; + copiedUserInfo.headwearEffect = [originalModel.userInfo.headwearEffect copy]; + copiedUserInfo.headwearPic = [originalModel.userInfo.headwearPic copy]; + copiedUserInfo.headWearUrl = [originalModel.userInfo.headWearUrl copy]; + copiedUserInfo.capUrl = [originalModel.userInfo.capUrl copy]; + copiedUserInfo.userInfoCardPic = [originalModel.userInfo.userInfoCardPic copy]; + copiedUserInfo.micCircle = [originalModel.userInfo.micCircle copy]; + copiedUserInfo.micNickColor = [originalModel.userInfo.micNickColor copy]; + copiedUserInfo.fromNick = [originalModel.userInfo.fromNick copy]; + copiedUserInfo.fromUid = [originalModel.userInfo.fromUid copy]; + copiedUserInfo.androidBubbleUrl = [originalModel.userInfo.androidBubbleUrl copy]; + copiedUserInfo.iosBubbleUrl = [originalModel.userInfo.iosBubbleUrl copy]; + copiedUserInfo.phoneAreaCode = [originalModel.userInfo.phoneAreaCode copy]; + copiedUserInfo.partitionId = [originalModel.userInfo.partitionId copy]; + copiedUserInfo.guildNameplateIcon = [originalModel.userInfo.guildNameplateIcon copy]; + copiedUserInfo.regionIcon = [originalModel.userInfo.regionIcon copy]; + copiedUserInfo.visitTimeDesc = [originalModel.userInfo.visitTimeDesc copy]; + + // 复制数组属性(浅复制,因为数组元素通常是不可变的) + copiedUserInfo.labels = [originalModel.userInfo.labels copy]; + copiedUserInfo.privatePhoto = [originalModel.userInfo.privatePhoto copy]; + copiedUserInfo.absCardPics = [originalModel.userInfo.absCardPics copy]; + copiedUserInfo.userGiftWall = [originalModel.userInfo.userGiftWall copy]; + copiedUserInfo.userLuckyBagGiftWall = [originalModel.userInfo.userLuckyBagGiftWall copy]; + copiedUserInfo.dynamicInfo = [originalModel.userInfo.dynamicInfo copy]; + copiedUserInfo.userNameplateList = [originalModel.userInfo.userNameplateList copy]; + copiedUserInfo.medalsPic = [originalModel.userInfo.medalsPic copy]; + + // 复制复杂对象属性(浅复制,因为这些对象通常不需要深度修改) + copiedUserInfo.userExpand = originalModel.userInfo.userExpand; + copiedUserInfo.userLevelVo = originalModel.userInfo.userLevelVo; + copiedUserInfo.userInfoSkillVo = originalModel.userInfo.userInfoSkillVo; + copiedUserInfo.userVipInfoVO = originalModel.userInfo.userVipInfoVO; + copiedUserInfo.audioCard = originalModel.userInfo.audioCard; + copiedUserInfo.medals = originalModel.userInfo.medals; + copiedUserInfo.relationUserVO = originalModel.userInfo.relationUserVO; + copiedUserInfo.guildInfo = originalModel.userInfo.guildInfo; + copiedUserInfo.usingPersonalBackground = originalModel.userInfo.usingPersonalBackground; + copiedUserInfo.infoCardVo = originalModel.userInfo.infoCardVo; + + // 复制属性字符串(这些是计算属性,直接复制) + copiedUserInfo.levelAtt = [originalModel.userInfo.levelAtt copy]; + copiedUserInfo.idAtt = [originalModel.userInfo.idAtt copy]; + + copiedModel.userInfo = copiedUserInfo; + } else { + copiedModel.userInfo = nil; + } + + [deepCopy setObject:copiedModel forKey:position]; + } + + return deepCopy; } - (void)onRoomUpdate { @@ -486,9 +621,7 @@ NSString* position = [data objectForKey:NIMChatroomEventInfoQueueChangeItemKey]; UserInfoModel* userInfo = [UserInfoModel modelWithJSON:[data objectForKey:NIMChatroomEventInfoQueueChangeItemValueKey]]; NSInteger changeType = [data[NIMChatroomEventInfoQueueChangeTypeKey] integerValue]; -// if (userInfo.uid == [AccountInfoStorage instance].getUid.integerValue) { -// ownerRTCChanged = YES; -// } + // 先清除该用户旧的麦位 for (MicroQueueModel *sequence in self.micQueue.allValues) { if (userInfo.uid == sequence.userInfo.uid) { @@ -496,7 +629,19 @@ } } RoomInfoModel *roomInfo = self.hostDelegate.getRoomInfo; - if (changeType == 2){ + if (changeType == 2){ // 下麦 + // 移除对应 position 的 MicroQueueModel 的 userInfo + MicroQueueModel *sequence = [self.micQueue objectForKey:position]; + if (sequence) { + sequence.userInfo = nil; + + // 确保 micQueue 更新后立即同步到视图 + UIView *microView = [self findMicroViewByIndex:[self positionToIndex:position]]; + if (microView) { + [microView configMicroView:sequence]; + } + } + if (userInfo.uid == [AccountInfoStorage instance].getUid.integerValue && roomInfo.roomModeType == RoomModeType_Open_Blind){ if (roomInfo.roomModeType != RoomModeType_Open_AcrossRoomPK_mode){ if ([RtcManager instance].isAnckorPk == NO){ @@ -510,6 +655,12 @@ }else if (changeType == 1) { // 上麦 MicroQueueModel *sequence = [self.micQueue objectForKey:position]; sequence.userInfo = userInfo; + + // 确保 micQueue 更新后立即同步到视图 + UIView *microView = [self findMicroViewByIndex:[self positionToIndex:position]]; + if (microView) { + [microView configMicroView:sequence]; + } if (self.hostDelegate.getRoomInfo.showGiftValue && userInfo.uid == [AccountInfoStorage instance].getUid.integerValue) { RoomInfoModel * roomInfo =self.hostDelegate.getRoomInfo; NSString * roomUid = [NSString stringWithFormat:@"%ld", roomInfo.uid]; @@ -546,9 +697,7 @@ } } } - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [RtcManager instance].isMiniEnter = NO; - }); + [RtcManager instance].isMiniEnter = NO; } } if(userInfo.isNoProhibitMic == YES){ @@ -590,6 +739,10 @@ case NIMChatroomEventTypeExit: case NIMChatroomEventTypeKicked: { + if (message.session.sessionId == self.hostDelegate.getRoomInfo.roomId) { + + } + for (NIMChatroomNotificationMember *member in content.targets) { for (MicroQueueModel *sequence in self.micQueue.allValues) { if (member.userId.integerValue == sequence.userInfo.uid) { @@ -1379,6 +1532,7 @@ } - (NSMutableDictionary *)micQueue { + // 可能是使用的對象不一致- stageview 和 當前選擇(比如 nineteenmicstageview)的保存 queue 內容不一致。 if (!_micQueue) { _micQueue= [NSMutableDictionary dictionary]; for (int i = 0; i < self.countOfMicroView; i++) { diff --git a/YuMi/Modules/YMRoom/View/XPRoomViewController.m b/YuMi/Modules/YMRoom/View/XPRoomViewController.m index ac3dff0a..21d0766a 100644 --- a/YuMi/Modules/YMRoom/View/XPRoomViewController.m +++ b/YuMi/Modules/YMRoom/View/XPRoomViewController.m @@ -3720,9 +3720,19 @@ XPCandyTreeInsufficientBalanceViewDelegate> return; } + if ([message.messageObject isKindOfClass:[NIMNotificationObject class]]) { + NIMNotificationObject *notiMsg = (NIMNotificationObject *)message.messageObject; + if ([notiMsg respondsToSelector:@selector(content)]) { + NIMChatroomNotificationContent *content = (NIMChatroomNotificationContent *)notiMsg.content; + + if (content.eventType == NIMChatroomEventTypeExit || content.eventType == NIMChatroomEventTypeKicked) { + return; + } + } + } + switch (message.messageType) { case NIMMessageTypeNotification: - [self handleNIMNotificationTypeMessage:message]; break; case NIMMessageTypeCustom: [self handleNimCustomTypeMessage:message];