更新房间功能,新增 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

@@ -554,6 +554,7 @@
4C886BEB2E014AE5006F0BA7 /* MedalsPresenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C886BEA2E014AE5006F0BA7 /* MedalsPresenter.m */; };
4C886BEE2E014B6C006F0BA7 /* Api+Medals.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C886BED2E014B6C006F0BA7 /* Api+Medals.m */; };
4C886BF22E015D61006F0BA7 /* MedalsModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C886BF12E015D61006F0BA7 /* MedalsModel.m */; };
4C9828132E6EB50000FC6345 /* MicCpInfoModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C9828122E6EB50000FC6345 /* MicCpInfoModel.m */; };
4CA532B42D5AEE9400B8F59F /* Api+LuckyPackage.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CA532B32D5AEE9400B8F59F /* Api+LuckyPackage.m */; };
4CA532B72D5B333200B8F59F /* RoomLuckyPackageInfoModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CA532B62D5B333200B8F59F /* RoomLuckyPackageInfoModel.m */; };
4CA532BA2D5C8EBE00B8F59F /* LuckyPackageBannerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CA532B92D5C8EBE00B8F59F /* LuckyPackageBannerView.m */; };
@@ -2783,6 +2784,8 @@
4C886BED2E014B6C006F0BA7 /* Api+Medals.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "Api+Medals.m"; sourceTree = "<group>"; };
4C886BF02E015D61006F0BA7 /* MedalsModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MedalsModel.h; sourceTree = "<group>"; };
4C886BF12E015D61006F0BA7 /* MedalsModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MedalsModel.m; sourceTree = "<group>"; };
4C9828112E6EB50000FC6345 /* MicCpInfoModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MicCpInfoModel.h; sourceTree = "<group>"; };
4C9828122E6EB50000FC6345 /* MicCpInfoModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MicCpInfoModel.m; sourceTree = "<group>"; };
4CA532B22D5AEE9400B8F59F /* Api+LuckyPackage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Api+LuckyPackage.h"; sourceTree = "<group>"; };
4CA532B32D5AEE9400B8F59F /* Api+LuckyPackage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "Api+LuckyPackage.m"; sourceTree = "<group>"; };
4CA532B52D5B333200B8F59F /* RoomLuckyPackageInfoModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoomLuckyPackageInfoModel.h; sourceTree = "<group>"; };
@@ -7923,6 +7926,8 @@
4C75CEFA2D6318FF009147A5 /* RoomEnterModel.m */,
4CFBE0C82DAD085700A923AF /* BravoGiftTabInfomationModel.h */,
4CFBE0C92DAD085700A923AF /* BravoGiftTabInfomationModel.m */,
4C9828112E6EB50000FC6345 /* MicCpInfoModel.h */,
4C9828122E6EB50000FC6345 /* MicCpInfoModel.m */,
);
path = Model;
sourceTree = "<group>";
@@ -12738,6 +12743,7 @@
E82325F2274E2DE6003A3332 /* XPUserCardViewController.m in Sources */,
4CA532B72D5B333200B8F59F /* RoomLuckyPackageInfoModel.m in Sources */,
E85E7B512A4EB0D300B6D00A /* Api+Guild.m in Sources */,
4C9828132E6EB50000FC6345 /* MicCpInfoModel.m in Sources */,
E83645682A40A2DC00E0DBE4 /* XPSkillCardPlayerManager.m in Sources */,
E8F65C222869A36F009BB5B9 /* ContentShareMonentsModel.m in Sources */,
9B6E856E281AABAB0041A321 /* XPRoomRecommendModel.m in Sources */,

View File

@@ -160,9 +160,6 @@ UIKIT_EXTERN NSString * adImageName;
info.identifier = emotionDic[@"id"];
info.image = image;
//
NSLog(@"加载表情: %@, 图片: %@, 是否成功: %@", info.displayName, emotionDic[@"file"], image ? @"是" : @"否");
[array addObject:info];
}
//
@@ -171,16 +168,7 @@ UIKIT_EXTERN NSString * adImageName;
// emoji
[QEmotionHelper clearEmojiCache];
#if DEBUG
//
NSLog(@"表情数组加载完成,总数: %lu", (unsigned long)array.count);
for (int i = 0; i < MIN(array.count, 3); i++) {
QEmotion *emotion = array[i];
NSLog(@"测试表情 %d: %@, 图片: %@", i, emotion.displayName, emotion.image ? @"加载成功" : @"加载失败");
}
});
#endif
}
#pragma mark - 广

View File

@@ -156,13 +156,19 @@ typedef NS_ENUM(NSUInteger, CustomMessageType) {
/// 客户端独立收发的消息, 从 1000 开始
ClientMessage_Type = 1000,
MicRelationship_Type = 1001,
};
typedef NS_ENUM(NSUInteger, ClientMessageType) {
ClientMessage_UpMic_Ask = 1001,
ClientMessage_UpMic_Agree = 1002,
ClientMessage_UpMic_Reject = 1003,
ClientMessage_UpMic_Ask = 10001,
ClientMessage_UpMic_Agree = 10002,
ClientMessage_UpMic_Reject = 10003,
};
typedef NS_ENUM(NSUInteger, MicRelationshipType) {
MicRelationship_CP = 10011,
};

View File

@@ -163,6 +163,9 @@ UIKIT_EXTERN NSString *kRequestTicket;
// self.isHavePermission = YES;
// return;
//#endif
// TODO:
ClientConfig *config = [ClientConfig shareConfig];
NSArray *uidList = config.configInfo.giveDiamondErbanNoList;
for (id uid in uidList) {

View File

@@ -232,6 +232,9 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)shareGen:(HttpRequestHelperCompletion)completion targetUid:(NSString *)targetUid;
+ (void)getRoomMicCpListByRoomUid:(HttpRequestHelperCompletion)completion roomUid:(NSString *)roomUid;
+ (void)getRoomMicCpListByUidList:(HttpRequestHelperCompletion)completion uidList:(NSString *)uidList;
@end
NS_ASSUME_NONNULL_END

View File

@@ -322,4 +322,13 @@
+ (void)shareGen:(HttpRequestHelperCompletion)completion targetUid:(NSString *)targetUid {
[self makeRequest:@"share/gen" method:HttpRequestHelperMethodPOST completion:completion, __FUNCTION__, targetUid, nil];
}
+ (void)getRoomMicCpListByRoomUid:(HttpRequestHelperCompletion)completion roomUid:(NSString *)roomUid {
[self makeRequest:@"room/mic/cp/listByRoomUid" method:HttpRequestHelperMethodGET completion:completion, __FUNCTION__, roomUid, nil];
}
+ (void)getRoomMicCpListByUidList:(HttpRequestHelperCompletion)completion uidList:(NSArray<NSString *> *)uidList {
[self makeRequest:@"room/mic/cp/listByUidList" method:HttpRequestHelperMethodGET completion:completion, __FUNCTION__, uidList, nil];
}
@end

View File

@@ -133,6 +133,7 @@
@property (nonatomic, strong) UILabel *rankName_3;
@property (nonatomic, strong) UILabel *rankTipsLabel;
@property (nonatomic, strong) UILabel *ratioLabel;
@end
@@ -438,6 +439,12 @@
make.center.mas_equalTo(progressNumberBG);
}];
[progressBG addSubview:self.ratioLabel];
[self.ratioLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(progressBG).offset(2);
make.centerY.mas_equalTo(progressBG);
}];
UIImageView *rocketsBG = [self rocketsBG];
[self.view addSubview:rocketsBG];
[rocketsBG mas_makeConstraints:^(MASConstraintMaker *make) {
@@ -681,6 +688,18 @@
progress = 0.1;
}
// label
NSString *ratioText = [NSString stringWithFormat:@"%@/%@",
@(boom.exp * boom.speed / 100.0).stringValue,
@(boom.exp).stringValue];
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:ratioText];
[attributedString addAttribute:NSStrokeColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(0, attributedString.length)];
[attributedString addAttribute:NSStrokeWidthAttributeName value:@(-2.0) range:NSMakeRange(0, attributedString.length)];
[attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:NSMakeRange(0, attributedString.length)];
[attributedString addAttribute:NSFontAttributeName value:kFontMedium(14) range:NSMakeRange(0, attributedString.length)];
self.ratioLabel.attributedText = attributedString;
[UIView animateWithDuration:0.2 animations:^{
[self.progressBar mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(kGetScaleWidth(180) * progress); //
@@ -1120,5 +1139,27 @@
return _rankTipsLabel;
}
- (UILabel *)ratioLabel {
if (!_ratioLabel) {
_ratioLabel = [UILabel labelInitWithText:@"0/0" font:kFontMedium(14) textColor:[UIColor blackColor]];
_ratioLabel.textAlignment = NSTextAlignmentCenter;
_ratioLabel.transform = CGAffineTransformMakeRotation(M_PI_2); // 90
//
_ratioLabel.layer.shadowColor = [UIColor whiteColor].CGColor;
_ratioLabel.layer.shadowOffset = CGSizeZero;
_ratioLabel.layer.shadowRadius = 3.0;
_ratioLabel.layer.shadowOpacity = 0.8;
_ratioLabel.layer.masksToBounds = NO;
//
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"0/0"];
[attributedString addAttribute:NSStrokeColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(0, attributedString.length)];
[attributedString addAttribute:NSStrokeWidthAttributeName value:@(-2.0) range:NSMakeRange(0, attributedString.length)];
_ratioLabel.attributedText = attributedString;
}
return _ratioLabel;
}
@end

View File

@@ -0,0 +1,20 @@
//
// MicCpInfoModel.h
// YuMi
//
// Created by P on 2025/9/8.
//
#import "PIBaseModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface MicCpInfoModel : PIBaseModel
@property (nonatomic, assign) NSInteger uid;
@property (nonatomic, assign) NSInteger cpLevel;
@property (nonatomic, assign) NSInteger loverUid;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,12 @@
//
// MicCpInfoModel.m
// YuMi
//
// Created by P on 2025/9/8.
//
#import "MicCpInfoModel.h"
@implementation MicCpInfoModel
@end

View File

@@ -82,6 +82,10 @@ NS_ASSUME_NONNULL_BEGIN
- (void)getShareLink:(NSString *)roomUid success:(void(^)(NSString *link))success failure:(void(^)(NSError *error))failure;
- (void)micCpListByRoomUid:(NSString *)roomUid;
- (void)micCpListByUidList:(NSArray<NSString *> *)uidList;
@end
NS_ASSUME_NONNULL_END

View File

@@ -25,6 +25,7 @@
#import "XPRedPacketModel.h"
#import "XPFreeGiftModel.h"
#import "BoomInfoModel.h"
#import "MicCpInfoModel.h"
///P
#import "XPRoomProtocol.h"
@@ -343,4 +344,29 @@
} showLoading:YES errorToast:YES] targetUid:roomUid];
}
- (void)micCpListByRoomUid:(NSString *)roomUid {
@kWeakify(self);
[Api getRoomMicCpListByRoomUid:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
@kStrongify(self);
if (code == 200) {
if ([[self getView] respondsToSelector:@selector(getMicCpListByRoomUidSuccess:)]) {
NSArray *cpList = [MicCpInfoModel modelsWithArray:data.data];
[[self getView] getMicCpListByRoomUidSuccess:cpList];
}
}
} roomUid:roomUid];
}
- (void)micCpListByUidList:(NSArray<NSString *> *)uidList {
NSString *uidsString = [uidList componentsJoinedByString:@","];
[Api getRoomMicCpListByUidList:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
if (code == 200) {
if ([[self getView] respondsToSelector:@selector(getMicCpListByUidListSuccess:)]) {
NSArray *cpList = [MicCpInfoModel modelsWithArray:data.data];
[[self getView] getMicCpListByUidListSuccess:cpList];
}
}
} uidList:uidsString];
}
@end

View File

@@ -10,7 +10,7 @@
NS_ASSUME_NONNULL_BEGIN
@class RoomInfoModel, UserInfoModel, NIMChatroom, FirstChargeRoomWindowModel, XPRedPacketModel, BoomInfoModel, BoomDetailModel;
@class RoomInfoModel, UserInfoModel, NIMChatroom, FirstChargeRoomWindowModel, XPRedPacketModel, BoomInfoModel, BoomDetailModel, MicCpInfoModel;
@protocol XPRoomProtocol <NSObject>
@@ -42,7 +42,13 @@ NS_ASSUME_NONNULL_BEGIN
-(void)getKickUserListSuccessWithList:(NSArray *)list;
- (void)getRoomBoomInfoSuccess:(NSArray <BoomDetailModel*> *)models;
- (void)getRoomBoomExplosionSuccess:(BoomInfoModel *)model;
- (void)getMicCpListByRoomUidSuccess:(NSArray <MicCpInfoModel *> *)cpList;
- (void)getMicCpListByUidListSuccess:(NSArray <MicCpInfoModel *> *)cpList;
@end
NS_ASSUME_NONNULL_END

View File

@@ -296,8 +296,6 @@ done:
self.chatFaceTabList = [ChatFaceResponse modelsWithArray:data];
NSString *cachePath = [self getFaceCachePath];
NSLog(@"-------------- 表情缓存路径: %@", cachePath);
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *cachedUrls = [[defaults objectForKey:@"SVGACachedUrls"] mutableCopy] ?: [NSMutableDictionary dictionary];
@@ -310,7 +308,6 @@ done:
// URL
if ([cachedUrls[faceVo.faceUrl] isEqualToString:fileName] &&
[[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSLog(@"已有 SVGA文件缓存%@", faceVo.faceUrl);
continue;
}
@@ -320,7 +317,6 @@ done:
cachedUrls[faceVo.faceUrl] = fileName;
[defaults setObject:cachedUrls forKey:@"SVGACachedUrls"];
[defaults synchronize];
NSLog(@"SVGA文件缓存成功%@ - %@", faceVo.faceUrl, filePath);
}
}];
}

View File

@@ -5,11 +5,12 @@
// Created by AI Assistant on 2024/12/19.
//
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class MicMidpointRectManager;
@class UIView;
@protocol MicMidpointRectManagerDelegate <NSObject>
@@ -23,6 +24,10 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, weak) id<MicMidpointRectManagerDelegate> delegate;
@property (nonatomic, weak) UIView *containerView;
/// 缓存当前房间的CP关系列表
@property (nonatomic, strong, readonly) NSArray *cachedCpList;
/// 缓存麦位position -> uid无用户为-1
@property (nonatomic, strong, readonly) NSDictionary<NSNumber *, NSNumber *> *micPositionToUid;
/// 初始化方法
- (instancetype)initWithContainerView:(UIView *)containerView;
@@ -38,12 +43,59 @@ NS_ASSUME_NONNULL_BEGIN
/// 移除指定位置的中点矩形
- (void)removeMidpointRectAtFrame:(CGRect)frame;
/// 播放指定位置的SVGA动画
/// 播放指定位置的随机SVGA动画
- (void)playSVGAAnimationAtFrame:(CGRect)frame;
/// 播放指定位置的指定资源名SVGA动画资源名不带后缀
- (void)playSVGAAnimationAtFrame:(CGRect)frame withNamed:(NSString *)resourceName;
/// 抽象在指定中点位置绘制关系位透明容器并根据CP列表与两端uid匹配后播放对应等级SVGA
/// - Parameters:
/// - frame: 中点关系位frame
/// - micPairText: 调试展示用的麦位对文本仅DEBUG下显示50%透明)
/// - leftUid: 左侧麦位用户uid0表示无
/// - rightUid: 右侧麦位用户uid0表示无
/// - cpList: CP关系列表MicCpInfoModel 数组)
- (void)addRelationshipAtFrame:(CGRect)frame
micPairText:(NSString *)micPairText
leftUid:(NSInteger)leftUid
rightUid:(NSInteger)rightUid
cpList:(NSArray *)cpList;
/// 基于用户UID清理相关的中点矩形和SVGA动画
/// - Parameter uids: 需要清理的用户UID数组
- (void)removeMidpointRectsForUids:(NSArray<NSNumber *> *)uids;
/// 设置并缓存CP列表
- (void)setCpListCache:(NSArray *)cpList;
/// 使用当前stageView重建麦位快照position->uid无用户记为-1
- (void)rebuildMicSnapshotWithStageView:(id)stageView micCount:(NSInteger)micCount;
/// 基于上一次快照与当前stageView的实际用户生成“变动用户+左右邻居”的uid列表字符串数组并更新快照
- (NSArray<NSString *> *)uidListForChangedMicWithStageView:(id)stageView micCount:(NSInteger)micCount;
/// 计算本次与快照的差异,返回 @{ @"added": NSArray<NSNumber*>, @"removed": NSArray<NSNumber*> } 并更新快照
- (NSDictionary<NSString *, NSArray<NSNumber *> *> *)diffMicChangeWithStageView:(id)stageView micCount:(NSInteger)micCount;
/// 从缓存中移除包含这些uid的所有CP关系
- (void)removeCpEntriesForUids:(NSArray<NSNumber *> *)uids;
/// 用新列表替换指定uid相关的CP关系先移除涉及这些uid的旧数据再合并新数据
- (void)mergeCpListCache:(NSArray *)cpList replaceForUids:(NSArray<NSNumber *> *)uids;
/// 停止所有SVGA动画
- (void)stopAllSVGAAnimations;
/// 处理下麦事件移除相关用户的CP关系和SVGA显示
/// - Parameters:
/// - downMicUids: 下麦用户的UID数组
/// - stageView: 当前舞台视图
/// - micCount: 麦位总数
- (void)handleDownMicEvent:(NSArray<NSNumber *> *)downMicUids
stageView:(id)stageView
micCount:(NSInteger)micCount;
@end
NS_ASSUME_NONNULL_END

View File

@@ -6,13 +6,22 @@
//
#import "MicMidpointRectManager.h"
#import "MicCpInfoModel.h"
#import <SVGA.h>
#import "StageView.h"
#import <UIKit/UIKit.h>
@class SVGAImageView;
@class SVGAParser;
@interface MicMidpointRectManager ()
@property (nonatomic, strong) NSMutableArray<UIView *> *midpointRects;
@property (nonatomic, strong) NSMutableDictionary<NSString *, SVGAImageView *> *svgaViews;
@property (nonatomic, strong) SVGAParser *svgaParser;
@property (nonatomic, strong) NSArray *cachedCpListInternal;
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, NSNumber *> *micPositionToUidInternal; // position->uid (-1 if empty)
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSDictionary *> *midpointRectInfo; // frameKey -> @{@"leftUid": @(uid), @"rightUid": @(uid)}
@end
@@ -24,31 +33,39 @@
_midpointRects = [NSMutableArray array];
_svgaViews = [NSMutableDictionary dictionary];
_svgaParser = [[SVGAParser alloc] init];
_micPositionToUidInternal = [NSMutableDictionary dictionary];
_midpointRectInfo = [NSMutableDictionary dictionary];
}
return self;
}
- (NSArray *)cachedCpList {
return self.cachedCpListInternal ?: @[];
}
- (NSDictionary<NSNumber *,NSNumber *> *)micPositionToUid {
return self.micPositionToUidInternal ?: @{};
}
- (void)addMidpointRectAtFrame:(CGRect)frame
micPairText:(NSString *)micPairText
autoPlaySVGA:(BOOL)autoPlaySVGA {
//
//
UIView *rectView = [[UIView alloc] initWithFrame:frame];
rectView.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.3];
rectView.layer.borderColor = [UIColor blueColor].CGColor;
rectView.layer.borderWidth = 2.0;
rectView.layer.cornerRadius = 8.0;
rectView.backgroundColor = [UIColor clearColor];
rectView.userInteractionEnabled = NO;
rectView.tag = 56002;
//
#if DEBUG
// DEBUG50%
UILabel *label = [[UILabel alloc] init];
label.text = micPairText;
label.textColor = [UIColor whiteColor];
label.textColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5];
label.font = [UIFont boldSystemFontOfSize:12];
label.textAlignment = NSTextAlignmentCenter;
label.frame = rectView.bounds;
[rectView addSubview:label];
#endif
//
[self.containerView addSubview:rectView];
@@ -62,6 +79,156 @@
NSLog(@"🔧 添加中点矩形: %@, frame: %@", micPairText, NSStringFromCGRect(frame));
}
- (void)addRelationshipAtFrame:(CGRect)frame
micPairText:(NSString *)micPairText
leftUid:(NSInteger)leftUid
rightUid:(NSInteger)rightUid
cpList:(NSArray *)cpList {
//
[self addMidpointRectAtFrame:frame micPairText:micPairText autoPlaySVGA:NO];
//
NSString *frameKey = NSStringFromCGRect(frame);
self.midpointRectInfo[frameKey] = @{
@"leftUid": @(leftUid),
@"rightUid": @(rightUid)
};
if (leftUid <= 0 || rightUid <= 0 || cpList.count == 0) {
return;
}
// SVGA
for (MicCpInfoModel *obj in cpList) {
BOOL match = (obj.uid == leftUid && obj.loverUid == rightUid) || (obj.uid == rightUid && obj.loverUid == leftUid);
if (match) {
NSInteger safeLevel = MAX(1, MIN(5, obj.cpLevel+1));
NSString *svgaName = [NSString stringWithFormat:@"mic_cp_lv%ld", (long)safeLevel];
[self playSVGAAnimationAtFrame:frame withNamed:svgaName];
break;
}
}
}
#pragma mark - Cache & Snapshot
- (void)setCpListCache:(NSArray *)cpList {
self.cachedCpListInternal = cpList ?: @[];
}
- (void)rebuildMicSnapshotWithStageView:(id)stageView micCount:(NSInteger)micCount {
[self.micPositionToUidInternal removeAllObjects];
for (NSInteger i = 0; i < micCount; i++) {
NSInteger uid = -1;
if ([stageView respondsToSelector:@selector(findMicroViewByIndex:)]) {
UIView *microView = [(StageView *)stageView findMicroViewByIndex:i];
if ([microView respondsToSelector:@selector(getUser)]) {
id userInfo = [microView performSelector:@selector(getUser)];
if (userInfo && [userInfo respondsToSelector:@selector(uid)]) {
uid = (NSInteger)[userInfo valueForKey:@"uid"];
if (uid <= 0) { uid = -1; }
}
}
}
self.micPositionToUidInternal[@(i)] = @(uid);
}
}
- (NSArray<NSString *> *)uidListForChangedMicWithStageView:(id)stageView micCount:(NSInteger)micCount {
NSMutableSet<NSNumber *> *result = [NSMutableSet set];
//
NSMutableArray<NSNumber *> *newUids = [NSMutableArray arrayWithCapacity:micCount];
for (NSInteger i = 0; i < micCount; i++) {
NSInteger uid = -1;
if ([stageView respondsToSelector:@selector(findMicroViewByIndex:)]) {
UIView *microView = [(StageView *)stageView findMicroViewByIndex:i];
if ([microView respondsToSelector:@selector(getUser)]) {
id userInfo = [microView performSelector:@selector(getUser)];
if (userInfo && [userInfo respondsToSelector:@selector(uid)]) {
uid = (NSInteger)[userInfo valueForKey:@"uid"];
if (uid <= 0) { uid = -1; }
}
}
}
[newUids addObject:@(uid)];
NSNumber *oldVal = self.micPositionToUidInternal[@(i)] ?: @(-1);
if (oldVal.integerValue != uid) {
//
if (uid > 0) { [result addObject:@(uid)]; }
NSInteger leftIdx = i - 1;
NSInteger rightIdx = i + 1;
if (leftIdx >= 0) {
NSInteger leftUid = (leftIdx < newUids.count) ? newUids[leftIdx].integerValue : -1;
if (leftUid > 0) { [result addObject:@(leftUid)]; }
}
if (rightIdx < micCount) {
NSInteger rightUid = (rightIdx < newUids.count) ? newUids[rightIdx].integerValue : -1;
if (rightUid > 0) { [result addObject:@(rightUid)]; }
}
}
}
//
for (NSInteger i = 0; i < micCount; i++) {
self.micPositionToUidInternal[@(i)] = newUids[i];
}
//
NSMutableArray<NSString *> *uids = [NSMutableArray arrayWithCapacity:result.count];
for (NSNumber *num in result) { [uids addObject:num.stringValue]; }
return uids;
}
- (NSDictionary<NSString *, NSArray<NSNumber *> *> *)diffMicChangeWithStageView:(id)stageView micCount:(NSInteger)micCount {
NSMutableArray<NSNumber *> *added = [NSMutableArray array];
NSMutableArray<NSNumber *> *removed = [NSMutableArray array];
for (NSInteger i = 0; i < micCount; i++) {
NSInteger uid = -1;
if ([stageView respondsToSelector:@selector(findMicroViewByIndex:)]) {
UIView *microView = [(StageView *)stageView findMicroViewByIndex:i];
if ([microView respondsToSelector:@selector(getUser)]) {
id userInfo = [microView performSelector:@selector(getUser)];
if (userInfo && [userInfo respondsToSelector:@selector(uid)]) {
uid = (NSInteger)[userInfo valueForKey:@"uid"];
if (uid <= 0) { uid = -1; }
}
}
}
NSInteger oldUid = (self.micPositionToUidInternal[@(i)] ?: @(-1)).integerValue;
if (uid != oldUid) {
if (oldUid > 0 && uid == -1) { //
[removed addObject:@(oldUid)];
} else if (uid > 0) { //
[added addObject:@(uid)];
}
}
self.micPositionToUidInternal[@(i)] = @(uid);
}
return @{ @"added": added.copy, @"removed": removed.copy };
}
- (void)removeCpEntriesForUids:(NSArray<NSNumber *> *)uids {
if (uids.count == 0 || self.cachedCpListInternal.count == 0) { return; }
NSMutableArray *filtered = [NSMutableArray array];
NSSet *uidSet = [NSSet setWithArray:uids];
for (id obj in self.cachedCpListInternal) {
NSInteger a = 0, b = 0;
if ([obj respondsToSelector:@selector(uid)]) { a = (NSInteger)[obj valueForKey:@"uid"]; }
if ([obj respondsToSelector:@selector(loverUid)]) { b = (NSInteger)[obj valueForKey:@"loverUid"]; }
if (![uidSet containsObject:@(a)] && ![uidSet containsObject:@(b)]) {
[filtered addObject:obj];
}
}
self.cachedCpListInternal = filtered.copy;
}
- (void)mergeCpListCache:(NSArray *)cpList replaceForUids:(NSArray<NSNumber *> *)uids {
// uid
[self removeCpEntriesForUids:uids];
if (cpList.count == 0) { return; }
//
NSMutableArray *merged = [self.cachedCpListInternal mutableCopy] ?: [NSMutableArray array];
[merged addObjectsFromArray:cpList];
self.cachedCpListInternal = merged.copy;
}
- (void)removeAllMidpointRects {
// SVGA
[self stopAllSVGAAnimations];
@@ -71,6 +238,7 @@
[rectView removeFromSuperview];
}
[self.midpointRects removeAllObjects];
[self.midpointRectInfo removeAllObjects];
NSLog(@"🔧 移除所有中点矩形");
}
@@ -94,6 +262,9 @@
}
}
//
[self.midpointRectInfo removeObjectForKey:frameKey];
NSLog(@"🔧 移除指定位置的中点矩形: %@", NSStringFromCGRect(frame));
}
@@ -101,40 +272,35 @@
// SVGA
NSArray *svgaFiles = @[@"mic_cp_lv1", @"mic_cp_lv2", @"mic_cp_lv3", @"mic_cp_lv4", @"mic_cp_lv5"];
NSString *randomSVGA = svgaFiles[arc4random_uniform((uint32_t)svgaFiles.count)];
// SVGA
NSString *svgaPath = [[NSBundle mainBundle] pathForResource:randomSVGA ofType:@"svga"];
[self playSVGAAnimationAtFrame:frame withNamed:randomSVGA];
}
// SVGA
- (void)playSVGAAnimationAtFrame:(CGRect)frame withNamed:(NSString *)resourceName {
if (resourceName.length == 0) { return; }
NSString *svgaPath = [[NSBundle mainBundle] pathForResource:resourceName ofType:@"svga"];
if (!svgaPath) {
NSLog(@"⚠️ 找不到SVGA文件: %@", randomSVGA);
NSLog(@"⚠️ 找不到SVGA文件: %@", resourceName);
return;
}
// SVGAImageView
SVGAImageView *svgaView = [[SVGAImageView alloc] initWithFrame:frame];
svgaView.contentMode = UIViewContentModeScaleAspectFit;
svgaView.userInteractionEnabled = NO;
svgaView.backgroundColor = [UIColor clearColor];
//
[self.containerView addSubview:svgaView];
// SVGA
NSString *frameKey = NSStringFromCGRect(frame);
self.svgaViews[frameKey] = svgaView;
// SVGA
[self.svgaParser parseWithURL:[NSURL fileURLWithPath:svgaPath]
[self.svgaParser parseWithURL:[NSURL fileURLWithPath:svgaPath]
completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) {
dispatch_async(dispatch_get_main_queue(), ^{
svgaView.videoItem = videoItem;
svgaView.loops = 0;
svgaView.loops = 0;
svgaView.clearsAfterStop = YES;
[svgaView startAnimation];
NSLog(@"🎬 开始播放SVGA动画: %@, frame: %@", randomSVGA, NSStringFromCGRect(frame));
NSLog(@"🎬 开始播放SVGA动画: %@, frame: %@", resourceName, NSStringFromCGRect(frame));
});
} failureBlock:^(NSError * _Nonnull error) {
NSLog(@"❌ SVGA动画解析失败: %@, error: %@", randomSVGA, error.localizedDescription);
NSLog(@"❌ SVGA动画解析失败: %@, error: %@", resourceName, error.localizedDescription);
[svgaView removeFromSuperview];
[self.svgaViews removeObjectForKey:frameKey];
}];
@@ -150,6 +316,60 @@
NSLog(@"🔧 停止所有SVGA动画");
}
- (void)removeMidpointRectsForUids:(NSArray<NSNumber *> *)uids {
if (uids.count == 0) {
NSLog(@"🔧 基于UID清理没有需要清理的用户跳过处理");
return;
}
NSLog(@"🔧 基于UID清理开始清理用户 %@ 相关的中点矩形和SVGA", uids);
NSSet<NSNumber *> *uidSet = [NSSet setWithArray:uids];
NSMutableArray<NSString *> *frameKeysToRemove = [NSMutableArray array];
//
for (NSString *frameKey in self.midpointRectInfo.allKeys) {
NSDictionary *rectInfo = self.midpointRectInfo[frameKey];
NSNumber *leftUid = rectInfo[@"leftUid"];
NSNumber *rightUid = rectInfo[@"rightUid"];
//
if ([uidSet containsObject:leftUid] || [uidSet containsObject:rightUid]) {
[frameKeysToRemove addObject:frameKey];
}
}
// SVGA
for (NSString *frameKey in frameKeysToRemove) {
CGRect frame = CGRectFromString(frameKey);
[self removeMidpointRectAtFrame:frame];
}
NSLog(@"🔧 基于UID清理完成移除了 %lu 个中点矩形和SVGA动画", (unsigned long)frameKeysToRemove.count);
}
- (void)handleDownMicEvent:(NSArray<NSNumber *> *)downMicUids
stageView:(id)stageView
micCount:(NSInteger)micCount {
if (downMicUids.count == 0) {
NSLog(@"🔧 处理下麦事件:没有下麦用户,跳过处理");
return;
}
NSLog(@"🔧 处理下麦事件:下麦用户 %@", downMicUids);
// 1. CP
[self removeCpEntriesForUids:downMicUids];
// 2. UIDSVGA
[self removeMidpointRectsForUids:downMicUids];
// 3.
[self rebuildMicSnapshotWithStageView:stageView micCount:micCount];
NSLog(@"🔧 下麦事件处理完成");
}
#pragma mark - Dealloc
- (void)dealloc {

View File

@@ -677,7 +677,6 @@
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;
SVGAParser *parse = [[SVGAParser alloc] init];
@kWeakify(self);
@@ -690,22 +689,18 @@
}
} 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;
}
@@ -827,23 +822,16 @@
- (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];
@@ -853,12 +841,9 @@
}
}
- (void)setHeadWearToFirstFrameOnly {
NSLog(@"🎮 MicroView: 设置头饰为只显示第一帧模式");
- (void)setHeadWearToFirstFrameOnly {
// YYAnimatedImageView
if (!self.headWearImageView.hidden) {
NSLog(@"🎮 MicroView: 停止 YYAnimatedImageView 动画");
[self.headWearImageView stopAnimating];
UIImage <YYAnimatedImage> *sprites = (YYSpriteSheetImage *)self.headWearImageView.image;
self.tempSprites = sprites;
@@ -866,36 +851,28 @@
if (firstFrame) {
self.headWearImageView.image = firstFrame;
}
} else
// SVGAImageView
if (!self.headWearSVGAImageView.hidden) {
NSLog(@"🎮 MicroView: 停止 SVGAImageView 动画");
} else if (!self.headWearSVGAImageView.hidden) {
// 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];

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