更新房间功能,新增 MicCpInfoModel 类以管理麦位关系数据,优化房间中点矩形和麦位状态管理逻辑。同时,更新相关 Presenter 和 ViewController 以集成新的麦位关系 API,提升代码可维护性和用户体验。新增麦位状态监测和更新机制,确保用户在房间中的麦位信息实时准确。

This commit is contained in:
edwinQQQ
2025-09-09 15:54:36 +08:00
parent 10d4abf5ee
commit 68088e00e9
17 changed files with 1053 additions and 145 deletions

View File

@@ -94,6 +94,10 @@
#import "RoomResourceManager.h"
#import "LuckyPackageLogicManager.h"
#import "MicCpInfoModel.h"
#import "XPMessageRemoteExtModel.h"
#import "MicMidpointRectManager.h"
// 🔧 Turbo Mode Tips
#import "XPTurboModeTipsManager.h"
#import "BuglyManager.h"
@@ -194,6 +198,10 @@ XPCandyTreeInsufficientBalanceViewDelegate>
///
@property(nonatomic,assign) BOOL isShowRedPacket;
@property(nonatomic,copy) NSString *releaseCoins;
// 🔧
@property (atomic, assign) BOOL isExitingRoom; // 退
@property (atomic, assign) BOOL isViewActive; // VC /
@property(nonatomic,copy) NSString *myCoins;
@property(nonatomic,strong) UIButton *exitGameButton;
@@ -204,6 +212,13 @@ XPCandyTreeInsufficientBalanceViewDelegate>
/// 🔧 block
@property(nonatomic,strong) id<NSObject> exchangeRoomAnimationViewObserver;
/// CPcpSVGA
@property (nonatomic, strong) NSArray<MicCpInfoModel *> *currentCpList;
@property (nonatomic, assign) BOOL currentUserMicStatusChanged; //
@property (nonatomic, assign) BOOL currentUserWasOnMic; //
@property (nonatomic, assign) NSInteger currentUserMicPosition; // -1
@property (nonatomic, assign) BOOL hasCompletedRoomInitialization; //
@end
@implementation XPRoomViewController
@@ -296,6 +311,13 @@ XPCandyTreeInsufficientBalanceViewDelegate>
NSLog(@"🔄 XPRoomViewController: 开始销毁");
// 🔧 TRTC
[[RtcManager instance] exitRoom];
// 🔧 退
self.isExitingRoom = YES;
self.isViewActive = NO;
[[RoomBoomManager sharedManager] leaveRoom];
[XPSkillCardPlayerManager shareInstance].photoIdList = nil;
@@ -552,6 +574,15 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[super viewWillDisappear:animated];
self.freeView.hidden = YES;
// 🔧 VC
self.isViewActive = NO;
// 🔧 VC dismiss TRTC
if (self.isMovingFromParentViewController || self.isBeingDismissed) {
[[RtcManager instance] exitRoom];
self.isExitingRoom = YES;
}
//
if ([[GiftComboManager sharedManager] isActive]) {
NSLog(@"📱 房间即将退出,检查连击状态");
@@ -564,6 +595,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
[XPSkillCardPlayerManager shareInstance].isInRoomVC = NO;
// 🔧
self.isViewActive = NO;
// 🔧 退 BuglyManager 退
[[NSNotificationCenter defaultCenter] postNotificationName:@"RoomDidExit"
object:nil
@@ -576,6 +610,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
[XPSkillCardPlayerManager shareInstance].isInRoomVC = YES;
// 🔧 VC
self.isViewActive = YES;
// 🔧 BuglyManager
[[NSNotificationCenter defaultCenter] postNotificationName:@"RoomDidEnter"
object:nil
@@ -634,7 +671,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[self.view addSubview:self.animationView];
// 🔧 Turbo Mode Tips
#if DEBUG
[self addTestButton];
#endif
}
- (void)__layoutTwentyMicStage {
@@ -1029,6 +1068,13 @@ XPCandyTreeInsufficientBalanceViewDelegate>
// 🔧 SocialStageView
[self drawSocialStageMidpointRects];
// 🔧 CPAPI
[self callMicCpListByRoomUidAfterRoomEntered];
// 🔧 stage viewCPAPI
NSMutableDictionary<NSString *,MicroQueueModel *> *currentQueue = [self.stageView getMicroQueue];
[self callMicCpListByUidListOnMicChangeWithQueue:currentQueue];
[self addExitGameButton];
@@ -1362,7 +1408,10 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[self.backContainerView onRoomEntered];
[self.littleGameView onRoomEntered];
if ([XPRoomMiniManager shareManager].getRoomInfo.uid != self.roomUid.integerValue) {// 退
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
// 🔧
if (!self.isExitingRoom && self.isViewActive) {
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
}
[self.functionView onRoomEntered];
[self.messageContainerView onRoomEntered];
[self.menuContainerView onRoomEntered];
@@ -1437,7 +1486,10 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[self.backContainerView onRoomEntered];
[self.littleGameView onRoomEntered];
if ([XPRoomMiniManager shareManager].getRoomInfo.uid != self.roomUid.integerValue) {// 退
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
// 🔧
if (!self.isExitingRoom && self.isViewActive) {
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
}
[self.functionView onRoomEntered];
[self.messageContainerView onRoomEntered];
[self.menuContainerView onRoomEntered];
@@ -1661,15 +1713,27 @@ XPCandyTreeInsufficientBalanceViewDelegate>
self.quickMessageContainerView.titleArray = self.roomInfo.speakTemplate;
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
// 🔧
if (!self.isExitingRoom && self.isViewActive) {
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
}
}
- (void)enterRoomSuccess:(NIMChatroom *)chatRoom {
[XNDJTDDLoadingTool hideHUDInView:self.navigationController.view];
// 🔧 VC
if (self.isExitingRoom || !self.isViewActive) {
NSLog(@"🔧 enterRoomSuccess: VC 已退出或不可见,忽略回调");
return;
}
[self.stageView onRoomEntered];
[self.functionView onRoomEntered];
// 🔧
[self initializeCurrentUserMicStatus];
//
if (self.roomInfo != nil) {
[self.presenter reportUserInterRoom:[NSString stringWithFormat:@"%zd", self.roomInfo.uid]];
@@ -1685,6 +1749,13 @@ XPCandyTreeInsufficientBalanceViewDelegate>
- (void)enterRoomFail:(NSInteger)code {
[XNDJTDDLoadingTool hideHUDInView:self.navigationController.view];
[self hideHUD];
// 🔧 VC
if (self.isExitingRoom || !self.isViewActive) {
NSLog(@"🔧 enterRoomFail: VC 已退出或不可见,忽略回调 (code: %ld)", (long)code);
return;
}
if (code == 13003) {
[self showErrorToast:YMLocalizedString(@"XPRoomViewController3")];
}
@@ -1851,7 +1922,10 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[self.backContainerView onRoomEntered];
[self.littleGameView onRoomEntered];
if ([XPRoomMiniManager shareManager].getRoomInfo.uid != self.roomUid.integerValue) {// 退
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
// 🔧
if (!self.isExitingRoom && self.isViewActive) {
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
}
[self.functionView onRoomEntered];
[self.messageContainerView onRoomEntered];
[self.menuContainerView onRoomEntered];
@@ -1882,7 +1956,6 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[self.presenter openRoom:title type:type roomPwd:@"" roomDesc:@"" backPic:@"" mgId:self.mgId];
}
} else { //
// TODO: 线
[self showSuccessToast:YMLocalizedString(@"XPRoomViewController7")];
[self enterRoomFail:0];
}
@@ -1994,21 +2067,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
}
if (![message.session.sessionId isEqualToString:@(self.roomInfo.roomId).stringValue]) {
// if (message.messageType == NIMMessageTypeCustom) {
// NIMCustomObject *obj = (NIMCustomObject *)message.messageObject;
// if ([obj.attachment isKindOfClass:[AttachmentModel class]]) {
// AttachmentModel *att = (AttachmentModel *)obj.attachment;
// if (!att.isFromPublic) {
// NSLog(@"[Recv] ⛔️ 过滤:房间不匹配 | msg.sid=%@ | curRoomId=%@",
// message.session.sessionId, @(self.roomInfo.roomId).stringValue);
// continue;
// }
// }
// }else {
NSLog(@"[Recv] ⛔️ 过滤:房间不匹配 | msg.sid=%@ | curRoomId=%@",
message.session.sessionId, @(self.roomInfo.roomId).stringValue);
continue;
// }
NSLog(@"[Recv] ⛔️ 过滤:房间不匹配 | msg.sid=%@ | curRoomId=%@",
message.session.sessionId, @(self.roomInfo.roomId).stringValue);
continue;
}
NSLog(@"[Recv] --- Message Raw Attach Content: %@, %@, %ld", @(message.senderClientType), message.rawAttachContent, (long)message.messageType);
@@ -2021,20 +2082,6 @@ XPCandyTreeInsufficientBalanceViewDelegate>
if (message.messageType == NIMMessageTypeNotification) {
[self handleNIMNotificationTypeMessage:message];
} else if (message.messageType == NIMMessageTypeCustom) {
// first/second/size3
#if DEBUG
if ([message.messageObject isKindOfClass:[NIMCustomObject class]]) {
NIMCustomObject *obj = (NIMCustomObject *)message.messageObject;
if ([obj.attachment isKindOfClass:[AttachmentModel class]]) {
AttachmentModel *att = (AttachmentModel *)obj.attachment;
NSData *payloadJSON = nil;
@try { payloadJSON = [NSJSONSerialization dataWithJSONObject:att.data ?: @{} options:0 error:nil]; } @catch (__unused NSException *e) {}
NSLog(@"[Recv] 🎯 自定义消息 | first=%ld second=%ld | payload=%lub | sid=%@ | ts=%.3f",
(long)att.first, (long)att.second, (unsigned long)payloadJSON.length,
message.session.sessionId, [[NSDate date] timeIntervalSince1970]);
}
}
#endif
[self handleNimCustomTypeMessage:message];
} else if(message.messageType == NIMMessageTypeText) {
[self.messageContainerView handleNIMTextMessage:message];
@@ -2158,7 +2205,6 @@ XPCandyTreeInsufficientBalanceViewDelegate>
//->->)
newRoomInfo.hadChangeRoomType = self.roomInfo.type != newRoomInfo.type;
BOOL anchorToOther = newRoomInfo.type != RoomType_Anchor && self.roomInfo.type == RoomType_Anchor;//
RoomType currentType = self.roomInfo.type;
self.roomInfo = newRoomInfo;
[self.backContainerView onRoomUpdate];
@@ -2424,10 +2470,78 @@ XPCandyTreeInsufficientBalanceViewDelegate>
break;
}
break;
case MicRelationship_Type:
switch (attachment.second) {
case MicRelationship_CP:
[self handleMicRelationshipCPMessage:attachment];
break;
}
break;
}
}
}
/// CP
- (void)handleMicRelationshipCPMessage:(AttachmentModel *)attachment {
NSLog(@"🔧 接收到麦位关系CP消息");
if (!attachment.data || ![attachment.data isKindOfClass:[NSString class]]) {
NSLog(@"⚠️ 麦位关系CP消息data格式错误跳过处理");
return;
}
NSString *jsonString = (NSString *)attachment.data;
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
if (error) {
NSLog(@"❌ 麦位关系CP消息JSON解析失败 - %@", error.localizedDescription);
return;
}
//
NSNumber *first = jsonDict[@"first"];
NSNumber *second = jsonDict[@"second"];
NSArray *dataArray = jsonDict[@"data"];
if (!first || !second || ![dataArray isKindOfClass:[NSArray class]]) {
NSLog(@"⚠️ 麦位关系CP消息消息格式错误跳过处理");
return;
}
if (first.integerValue != MicRelationship_Type || second.integerValue != MicRelationship_CP) {
NSLog(@"⚠️ 麦位关系CP消息消息类型不匹配跳过处理");
return;
}
// CP
NSMutableArray<MicCpInfoModel *> *cpList = [NSMutableArray array];
for (NSDictionary *cpDict in dataArray) {
if ([cpDict isKindOfClass:[NSDictionary class]]) {
MicCpInfoModel *cpInfo = [[MicCpInfoModel alloc] init];
cpInfo.uid = [cpDict[@"uid"] integerValue];
cpInfo.loverUid = [cpDict[@"loverUid"] integerValue];
cpInfo.cpLevel = [cpDict[@"cpLevel"] integerValue];
[cpList addObject:cpInfo];
}
}
NSLog(@"🔧 麦位关系CP消息解析到 %lu 条CP数据", (unsigned long)cpList.count);
// CP
self.currentCpList = cpList.copy;
// MicMidpointRectManager
[self updateMicMidpointRectManagerCache:cpList];
// CP SVGA
[self drawSocialStageMidpointRects];
NSLog(@"🔧 麦位关系CP消息处理完成");
}
- (void)handleUpMicAsk:(AttachmentModel *)attachment {
NSNumber *targetUid = [attachment.data objectForKey:@"targetUid"];
if (!targetUid || ![targetUid.stringValue isEqualToString:[AccountInfoStorage instance].getUid]) {
@@ -2731,6 +2845,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
- (void)exitRoom {
[XNDJTDDLoadingTool showLoading];
// 🔧 退
self.isExitingRoom = YES;
[XPSkillCardPlayerManager shareInstance].micState = MICState_None;
[self.stageView exitNIMRoom];
@@ -2849,7 +2966,14 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[self.littleGameView destroyMG];
}
[[XPRoomMiniManager shareManager] resetLocalMessage];
// 🔧 TRTC 退
[[RtcManager instance] exitRoom];
// 🔧 退
self.isExitingRoom = YES;
self.isViewActive = NO;
[self.presenter exitNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId]];
[self.presenter reportUserOutRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.uid]];
[self handleFirstOutRoom];
@@ -2946,19 +3070,31 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[self.functionView onRoomUpdate];
[self.functionView onMicroQueueUpdate:queue];
BOOL isOnMic = false;
for (MicroQueueModel * info in queue.allValues) {
if (info.userInfo.uid > 0 && [AccountInfoStorage instance].getUid.integerValue == info.userInfo.uid) {
isOnMic = YES;
break;
}
}
//
[self updateCurrentUserMicStatus:queue];
//
NSDictionary *currentStatus = [self getCurrentUserMicStatus:queue];
BOOL isOnMic = [currentStatus[@"isOnMic"] boolValue];
if (isOnMic) {
self.anchorScrollView.scrollEnabled = NO;
} else {
self.anchorScrollView.scrollEnabled = YES;
}
// 🔧 micAPI
if (self.hasCompletedRoomInitialization) {
// CP
[self handleDownMicEventIfNeeded:queue];
// 🔧
[self updateMicMidpointRectManagerSnapshot];
[self callMicCpListByUidListOnMicChangeWithQueue:queue];
} else {
NSLog(@"🔧 进房初始化中,跳过 micCpListByUidList 调用");
}
}
- (CGPoint)animationPointAtStageViewByUid:(NSString *)uid {
@@ -2978,6 +3114,28 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[[RoomBoomManager sharedManager] receiveEnterRoomBoom:model];
}
- (void)getMicCpListByRoomUidSuccess:(NSArray <MicCpInfoModel *> *)cpList {
self.currentCpList = cpList;
// CPSVGA
[self drawSocialStageMidpointRects];
}
- (void)getMicCpListByUidListSuccess:(NSArray<MicCpInfoModel *> *)cpList {
self.currentCpList = cpList;
// 🔧 MicMidpointRectManager
[self updateMicMidpointRectManagerCache:cpList];
// CPSVGA
[self drawSocialStageMidpointRects];
// NIM message
if (self.currentUserMicStatusChanged) {
[self sendMicRelationshipNIMessage:cpList];
self.currentUserMicStatusChanged = NO; //
}
}
#pragma mark - 退
- (void)handleFirstOutRoom {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
@@ -3056,7 +3214,10 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[self.littleGameView onRoomEntered];
[self.littleGameView onRoomEntered];
if ([XPRoomMiniManager shareManager].getRoomInfo.uid != self.roomUid.integerValue) {// 退
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
// 🔧
if (!self.isExitingRoom && self.isViewActive) {
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
}
[self.functionView onRoomEntered];
[self.messageContainerView onRoomEntered];
[self.menuContainerView onRoomEntered];
@@ -3080,7 +3241,10 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[self.backContainerView onRoomEntered];
[self.littleGameView onRoomEntered];
[self.functionView onRoomEntered];
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
// 🔧
if (!self.isExitingRoom && self.isViewActive) {
[self.presenter enterNIMRoom:[NSString stringWithFormat:@"%ld", self.roomInfo.roomId] user:self.userInfo];
}
[self.messageContainerView onRoomEntered];
[self.littleGameView onRoomEntered];
[[XPRoomMiniManager shareManager] configRoomInfo:nil];
@@ -3320,13 +3484,6 @@ XPCandyTreeInsufficientBalanceViewDelegate>
return;
}
// 使
// [self.messageContainerView handleNIMCustomMessage:message];
// [self.animationView handleNIMCustomMessage:message];
// [self handleNimCustomTypeMessage:message];
// [self onRecvMessages:@[message]];
switch (message.messageType) {
case NIMMessageTypeNotification:
[self handleNIMNotificationTypeMessage:message];
@@ -3416,7 +3573,7 @@ XPCandyTreeInsufficientBalanceViewDelegate>
NSLog(@"🎮 卡顿检测模拟已触发,计数将增加");
}
/// StageView
/// StageView
- (void)drawSocialStageMidpointRects {
if (!self.stageView) {
NSLog(@"🔧 当前没有 stageView跳过中点矩形绘制");
@@ -3485,13 +3642,13 @@ XPCandyTreeInsufficientBalanceViewDelegate>
CGRect rect = [self.stageView rectForMidpointBetweenMicAtIndex:firstIndex andIndex:secondIndex];
if (!CGRectIsEmpty(rect)) {
UIView *debugView = [[UIView alloc] initWithFrame:rect];
debugView.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.3];
debugView.layer.borderColor = [UIColor blueColor].CGColor;
debugView.layer.borderWidth = 2.0;
debugView.layer.cornerRadius = 8.0;
debugView.tag = 56002;
debugView.userInteractionEnabled = NO;
UIView *micRelationshipView = [[UIView alloc] initWithFrame:rect];
micRelationshipView.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.3];
micRelationshipView.layer.borderColor = [UIColor blueColor].CGColor;
micRelationshipView.layer.borderWidth = 2.0;
micRelationshipView.layer.cornerRadius = 8.0;
micRelationshipView.tag = 56002;
micRelationshipView.userInteractionEnabled = NO;
//
UILabel *label = [[UILabel alloc] init];
@@ -3499,23 +3656,22 @@ XPCandyTreeInsufficientBalanceViewDelegate>
label.textColor = [UIColor whiteColor];
label.font = [UIFont boldSystemFontOfSize:12];
label.textAlignment = NSTextAlignmentCenter;
label.frame = debugView.bounds;
[debugView addSubview:label];
label.frame = micRelationshipView.bounds;
[micRelationshipView addSubview:label];
// StageView
if ([self.stageView isKindOfClass:[LittleGameScrollStageView class]]) {
[(LittleGameScrollStageView *)self.stageView addMidpointRect:debugView];
} else if ([self.stageView isKindOfClass:[LittleGameStageView class]]) {
[(LittleGameStageView *)self.stageView addMidpointRect:debugView];
} else if ([self.stageView respondsToSelector:@selector(midpointRectManager)]) {
// 使
// midpointRectManager SVGA退
if ([self.stageView respondsToSelector:@selector(midpointRectManager)]) {
id manager = [self.stageView valueForKey:@"midpointRectManager"];
if ([manager respondsToSelector:@selector(addMidpointRectAtFrame:micPairText:autoPlaySVGA:)]) {
if ([manager respondsToSelector:@selector(addRelationshipAtFrame:micPairText:leftUid:rightUid:cpList:)]) {
NSString *micPairText = [NSString stringWithFormat:@"%ld-%ld", (long)firstIndex, (long)secondIndex];
[manager addMidpointRectAtFrame:rect micPairText:micPairText autoPlaySVGA:YES];
UIView<MicroViewProtocol> *leftView = [self.stageView findMicroViewByIndex:firstIndex];
UIView<MicroViewProtocol> *rightView = [self.stageView findMicroViewByIndex:secondIndex];
NSInteger leftUid = [[leftView getUser] uid];
NSInteger rightUid = [[rightView getUser] uid];
[manager addRelationshipAtFrame:rect micPairText:micPairText leftUid:leftUid rightUid:rightUid cpList:self.currentCpList ?: @[]];
}
} else {
[self.stageView addSubview:debugView];
[self.stageView addSubview:micRelationshipView];
}
NSLog(@"🔧 绘制中点矩形: %ld-%ld, rect: %@", (long)firstIndex, (long)secondIndex, NSStringFromCGRect(rect));
} else {
@@ -3526,4 +3682,387 @@ XPCandyTreeInsufficientBalanceViewDelegate>
NSLog(@"🔧 %@ 中点矩形绘制完成", stageViewClass);
}
/// CPAPI
- (void)callMicCpListByRoomUidAfterRoomEntered {
NSLog(@"🔧 进房成功:开始调用 micCpListByRoomUid API");
if (!self.roomInfo) {
NSLog(@"⚠️ 进房成功roomInfo为空跳过CP API调用");
return;
}
NSString *roomUid = [NSString stringWithFormat:@"%ld", (long)self.roomInfo.uid];
NSLog(@"🔧 进房成功房间UID: %@, 房间类型: %ld", roomUid, (long)self.roomInfo.type);
// micCpListByRoomUid
if ([self.presenter respondsToSelector:@selector(micCpListByRoomUid:)]) {
NSLog(@"🔧 进房成功:调用 micCpListByRoomUid: %@", roomUid);
[self.presenter micCpListByRoomUid:roomUid];
} else {
NSLog(@"⚠️ 进房成功presenter不支持micCpListByRoomUid方法");
}
NSLog(@"🔧 进房成功micCpListByRoomUid API调用完成");
}
/// uid3
- (NSArray<NSString *> *)getAllMicUserUidsWithQueue:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
NSMutableArray<NSString *> *micUserUids = [NSMutableArray array];
if (!self.roomInfo) {
NSLog(@"⚠️ 获取mic用户列表roomInfo为空");
return micUserUids;
}
//
NSInteger currentUserMicPosition = self.currentUserMicPosition;
if (currentUserMicPosition == -1) {
NSLog(@"🔧 获取mic用户列表当前用户不在麦上返回空数据");
return micUserUids;
}
//
NSInteger maxMicCount = 0;
switch (self.roomInfo.type) {
case RoomType_Game:
maxMicCount = 9;
break;
case RoomType_10Mic:
maxMicCount = 10;
break;
case RoomType_15Mic:
maxMicCount = 15;
break;
case RoomType_19Mic:
maxMicCount = 19;
break;
case RoomType_20Mic:
maxMicCount = 20;
break;
default:
maxMicCount = 9; // 9
break;
}
// UID
NSInteger leftPosition = currentUserMicPosition - 1;
NSInteger rightPosition = currentUserMicPosition + 1;
//
if (leftPosition >= 0) {
NSString *leftPositionKey = [NSString stringWithFormat:@"%ld", (long)leftPosition];
MicroQueueModel *leftMicModel = queue[leftPositionKey];
if (leftMicModel && leftMicModel.userInfo && leftMicModel.userInfo.uid > 0) {
[micUserUids addObject:[NSString stringWithFormat:@"%ld", (long)leftMicModel.userInfo.uid]];
}
}
//
NSString *currentPositionKey = [NSString stringWithFormat:@"%ld", (long)currentUserMicPosition];
MicroQueueModel *currentMicModel = queue[currentPositionKey];
if (currentMicModel && currentMicModel.userInfo && currentMicModel.userInfo.uid > 0) {
[micUserUids addObject:[NSString stringWithFormat:@"%ld", (long)currentMicModel.userInfo.uid]];
}
//
if (rightPosition < maxMicCount) {
NSString *rightPositionKey = [NSString stringWithFormat:@"%ld", (long)rightPosition];
MicroQueueModel *rightMicModel = queue[rightPositionKey];
if (rightMicModel && rightMicModel.userInfo && rightMicModel.userInfo.uid > 0) {
[micUserUids addObject:[NSString stringWithFormat:@"%ld", (long)rightMicModel.userInfo.uid]];
}
}
//
if (micUserUids.count == 1) {
NSLog(@"🔧 获取mic用户列表只有当前用户一个人返回空数据");
return [NSArray array];
}
NSLog(@"🔧 获取mic用户列表当前用户麦位 %ld找到 %lu 个相关用户: %@",
(long)currentUserMicPosition, (unsigned long)micUserUids.count, micUserUids);
return micUserUids;
}
///
- (void)handleDownMicEventIfNeeded:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
if (!self.stageView || ![self.stageView respondsToSelector:@selector(midpointRectManager)]) {
NSLog(@"🔧 处理下麦事件stageView 不支持 midpointRectManager跳过处理");
return;
}
//
NSInteger micCount = 0;
if (self.roomInfo) {
switch (self.roomInfo.type) {
case RoomType_Game: micCount = 9; break;
case RoomType_10Mic: micCount = 10; break;
case RoomType_15Mic: micCount = 15; break;
case RoomType_19Mic: micCount = 19; break;
case RoomType_20Mic: micCount = 20; break;
default: micCount = 9; break;
}
}
// 使 MicMidpointRectManager
// MicMidpointRectManager *midpointRectManager = [self.stageView midpointRectManager];
MicMidpointRectManager *midpointRectManager = (MicMidpointRectManager *)[self.stageView performSelector:@selector(midpointRectManager)];
if (midpointRectManager) {
NSDictionary<NSString *, NSArray<NSNumber *> *> *micChanges =
[midpointRectManager diffMicChangeWithStageView:self.stageView micCount:micCount];
NSArray<NSNumber *> *removedUids = micChanges[@"removed"];
if (removedUids && removedUids.count > 0) {
NSLog(@"🔧 检测到下麦用户:%@", removedUids);
// MicMidpointRectManager
[midpointRectManager handleDownMicEvent:removedUids stageView:self.stageView micCount:micCount];
}
}
}
/// micCPAPI
- (void)callMicCpListByUidListOnMicChangeWithQueue:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
NSLog(@"🔧 mic位变动开始调用 micCpListByUidList API");
NSArray<NSString *> *micUserUids = [self getAllMicUserUidsWithQueue:queue];
if (micUserUids.count == 0) {
NSLog(@"🔧 mic位变动没有相关用户在mic上跳过API调用");
return;
}
// micCpListByUidList
if ([micUserUids containsObject:[AccountInfoStorage instance].getUid]) {
NSLog(@"🔧 mic位变动且包含了当前用户调用 micCpListByUidList: %@", micUserUids);
[self.presenter micCpListByUidList:micUserUids];
}
NSLog(@"🔧 mic位变动micCpListByUidList API调用完成");
}
///
- (void)initializeCurrentUserMicStatus {
NSLog(@"🔧 初始化当前用户麦位状态");
//
self.currentUserMicStatusChanged = NO;
self.currentUserWasOnMic = NO;
self.currentUserMicPosition = -1;
self.hasCompletedRoomInitialization = NO; //
//
NSMutableDictionary<NSString *,MicroQueueModel *> *currentQueue = [self.stageView getMicroQueue];
if (currentQueue && currentQueue.count > 0) {
NSDictionary *currentStatus = [self getCurrentUserMicStatus:currentQueue];
BOOL isOnMic = [currentStatus[@"isOnMic"] boolValue];
NSInteger micPosition = [currentStatus[@"micPosition"] integerValue];
self.currentUserWasOnMic = isOnMic;
self.currentUserMicPosition = micPosition;
NSLog(@"🔧 初始化完成 - 当前用户是否在麦上: %@, 麦位: %ld",
isOnMic ? @"是" : @"否", (long)micPosition);
} else {
NSLog(@"🔧 初始化完成 - 当前没有麦位数据");
}
// API
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.hasCompletedRoomInitialization = YES;
NSLog(@"🔧 进房初始化完成,后续麦位变动将触发 micCpListByUidList 调用");
});
}
///
- (NSDictionary *)getCurrentUserMicStatus:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
NSInteger currentUid = [AccountInfoStorage instance].getUid.integerValue;
BOOL isOnMic = NO;
NSInteger micPosition = -1;
for (NSString *position in queue.allKeys) {
MicroQueueModel *micModel = queue[position];
if (micModel.userInfo.uid > 0 && micModel.userInfo.uid == currentUid) {
isOnMic = YES;
micPosition = position.integerValue;
break;
}
}
return @{
@"isOnMic": @(isOnMic),
@"micPosition": @(micPosition)
};
}
///
- (BOOL)checkCurrentUserMicStatusChanged:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
NSDictionary *currentStatus = [self getCurrentUserMicStatus:queue];
BOOL currentUserOnMic = [currentStatus[@"isOnMic"] boolValue];
NSInteger currentMicPosition = [currentStatus[@"micPosition"] integerValue];
// 1
if (currentUserOnMic && !self.currentUserWasOnMic) {
NSLog(@"🔧 检测到当前用户刚刚上麦,麦位: %ld", (long)currentMicPosition);
return YES;
}
// 2
if (!currentUserOnMic && self.currentUserWasOnMic) {
NSLog(@"🔧 检测到当前用户刚刚下麦,之前麦位: %ld", (long)self.currentUserMicPosition);
return YES;
}
// 3
if (currentUserOnMic && self.currentUserWasOnMic &&
currentMicPosition != self.currentUserMicPosition) {
NSLog(@"🔧 检测到当前用户换麦位,从 %ld 换到 %ld",
(long)self.currentUserMicPosition, (long)currentMicPosition);
return YES;
}
return NO;
}
///
- (void)updateCurrentUserMicStatus:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
NSDictionary *currentStatus = [self getCurrentUserMicStatus:queue];
BOOL currentUserOnMic = [currentStatus[@"isOnMic"] boolValue];
NSInteger micPosition = [currentStatus[@"micPosition"] integerValue];
//
if ([self checkCurrentUserMicStatusChanged:queue]) {
self.currentUserMicStatusChanged = YES;
}
//
self.currentUserWasOnMic = currentUserOnMic;
self.currentUserMicPosition = micPosition;
}
/// NIM message
- (void)sendMicRelationshipNIMessage:(NSArray<MicCpInfoModel *> *)cpList {
NSLog(@"🔧 发送麦位关系 NIM messageCP数据条数: %ld当前用户麦位: %ld",
(long)cpList.count, (long)self.currentUserMicPosition);
if (!self.roomInfo) {
NSLog(@"⚠️ 发送麦位关系 NIM messageroomInfo为空跳过发送");
return;
}
// CP JSON string
NSMutableArray *cpDataArray = [NSMutableArray array];
for (MicCpInfoModel *cpInfo in cpList) {
NSDictionary *cpDict = @{
@"uid": @(cpInfo.uid),
@"loverUid": @(cpInfo.loverUid),
@"cpLevel": @(cpInfo.cpLevel)
};
[cpDataArray addObject:cpDict];
}
NSDictionary *finalData = @{@"first": @(MicRelationship_Type),
@"second":@(MicRelationship_CP),
@"data":cpDataArray.copy};
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:finalData options:0 error:&error];
if (error) {
NSLog(@"❌ 发送麦位关系 NIM messageJSON序列化失败 - %@", error.localizedDescription);
return;
}
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(@"🔧 发送麦位关系 NIM messageJSON数据 - %@", jsonString);
// AttachmentModel
AttachmentModel *attachment = [[AttachmentModel alloc] init];
attachment.first = MicRelationship_Type; // 1001
attachment.second = MicRelationship_CP; // 10011
attachment.data = jsonString;
// NIM message
NIMMessage *message = [[NIMMessage alloc] init];
NIMCustomObject *object = [[NIMCustomObject alloc] init];
object.attachment = attachment;
message.messageObject = object;
// remoteExt
UserInfoModel *userInfo = [self getUserInfo];
XPMessageRemoteExtModel *extModel = [[XPMessageRemoteExtModel alloc] init];
extModel.androidBubbleUrl = userInfo.androidBubbleUrl;
extModel.iosBubbleUrl = userInfo.iosBubbleUrl;
extModel.fromSayHelloChannel = userInfo.fromSayHelloChannel;
extModel.platformRole = userInfo.platformRole;
NSMutableDictionary *remoteExt = [NSMutableDictionary dictionaryWithObject:extModel.model2dictionary forKey:[NSString stringWithFormat:@"%ld", userInfo.uid]];
message.remoteExt = remoteExt;
//
NSString *sessionID = [NSString stringWithFormat:@"%ld", self.roomInfo.roomId];
NIMSession *session = [NIMSession session:sessionID type:NIMSessionTypeChatroom];
//
[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session completion:^(NSError * _Nullable error) {
if (error) {
NSLog(@"❌ 发送麦位关系 NIM message 失败 - %@", error.localizedDescription);
} else {
NSLog(@"✅ 发送麦位关系 NIM message 成功");
}
}];
}
/// MicMidpointRectManager
- (void)updateMicMidpointRectManagerCache:(NSArray<MicCpInfoModel *> *)cpList {
if (!self.stageView) {
NSLog(@"<22><> 更新缓存stageView 为空,跳过缓存更新");
return;
}
MicMidpointRectManager *midpointRectManager = (MicMidpointRectManager *)[self.stageView performSelector:@selector(midpointRectManager)];
if (midpointRectManager) {
// UID
NSMutableDictionary<NSString *,MicroQueueModel *> *currentQueue = [self.stageView getMicroQueue];
NSArray<NSString *> *micUserUids = [self getAllMicUserUidsWithQueue:currentQueue];
// NSNumber
NSMutableArray<NSNumber *> *uidNumbers = [NSMutableArray array];
for (NSString *uidString in micUserUids) {
[uidNumbers addObject:@(uidString.integerValue)];
}
//
[midpointRectManager mergeCpListCache:cpList replaceForUids:uidNumbers];
NSLog(@"<22><> 更新缓存完成CP数据条数 %lu相关用户 %@",
(unsigned long)cpList.count, uidNumbers);
}
}
/// MicMidpointRectManager
- (void)updateMicMidpointRectManagerSnapshot {
if (!self.stageView) {
NSLog(@"<22><> 更新快照stageView 为空,跳过快照更新");
return;
}
//
NSInteger micCount = 0;
if (self.roomInfo) {
switch (self.roomInfo.type) {
case RoomType_Game: micCount = 9; break;
case RoomType_10Mic: micCount = 10; break;
case RoomType_15Mic: micCount = 15; break;
case RoomType_19Mic: micCount = 19; break;
case RoomType_20Mic: micCount = 20; break;
default: micCount = 9; break;
}
}
MicMidpointRectManager *midpointRectManager = (MicMidpointRectManager *)[self.stageView performSelector:@selector(midpointRectManager)];
if (midpointRectManager) {
[midpointRectManager rebuildMicSnapshotWithStageView:self.stageView micCount:micCount];
NSLog(@"🔧 更新麦位快照完成:麦位总数 %ld", (long)micCount);
}
}
@end