843 lines
30 KiB
Objective-C
843 lines
30 KiB
Objective-C
//
|
||
// GiftComboManager.m
|
||
// YuMi
|
||
//
|
||
// Created by P on 2024/9/5.
|
||
//
|
||
// 处理连击面板逻辑
|
||
|
||
#import "GiftComboManager.h"
|
||
|
||
#import <Foundation/Foundation.h>
|
||
#import <UIKit/UIKit.h>
|
||
#import <Bugly/Bugly.h>
|
||
|
||
#import "Api+Gift.h"
|
||
|
||
#import "UserInfoModel.h"
|
||
#import "AttachmentModel.h"
|
||
#import "XPGiftCountModel.h"
|
||
#import "GiftReceiveInfoModel.h"
|
||
#import "XPMessageRemoteExtModel.h"
|
||
|
||
#import "GiftComboFlagView.h"
|
||
|
||
// 通知常量实现
|
||
NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotification";
|
||
|
||
@interface GiftComboManager ()
|
||
|
||
@property (nonatomic, assign) BOOL enableCombo;
|
||
|
||
@property (nonatomic, strong) NSMutableArray *requestQueue;
|
||
|
||
// 用来存储 GiftReceiveInfoModel 和 NSDictionary 的元数据队列
|
||
@property (nonatomic, strong) NSMutableArray *giftComboQueue;
|
||
@property (nonatomic, strong) NSMutableArray *comboFlagQueue;
|
||
@property (nonatomic, strong) dispatch_source_t comboFlagTimer;
|
||
@property (nonatomic, strong) UIView *containerView;
|
||
|
||
// 定时器,处理请求的调度器
|
||
@property (nonatomic, strong) dispatch_source_t timer;
|
||
|
||
@property (nonatomic, copy) NSArray *sendGiftToUIDs;
|
||
@property (nonatomic, assign) GiftSourceType giftSourceType;
|
||
@property (nonatomic, strong) GiftInfoModel *giftInfo;
|
||
@property (nonatomic, assign) RoomSendGiftType roomSendGiftType;
|
||
@property (nonatomic, copy) NSString *roomUID;
|
||
@property (nonatomic, copy) NSString *giftNumPerTimes;
|
||
@property (nonatomic, strong) UserInfoModel *sendGiftUserInfo;
|
||
@property (nonatomic, copy) NSString *sessionID;
|
||
@property (nonatomic, strong) XPGiftCountModel *countModel;
|
||
|
||
@property (nonatomic, assign) NSInteger combo;
|
||
@property (nonatomic, assign) bool isCombing;
|
||
|
||
@property (nonatomic, copy) void (^actionCallback)(ComboActionType type);
|
||
|
||
@property (nonatomic, copy) NSString *errorMessage;
|
||
|
||
@property (nonatomic, strong) NSMutableArray<GiftComboFlagView *> *activeViews; // 用于存储最多2个活跃的动画视图
|
||
|
||
@end
|
||
|
||
@implementation GiftComboManager
|
||
|
||
#pragma mark - 单例方法
|
||
|
||
- (void)dealloc {
|
||
[self stopProcessingGiftComboFlagQueue];
|
||
if (self.comboFlagQueue) {
|
||
self.comboFlagQueue = NULL;
|
||
}
|
||
}
|
||
|
||
+ (instancetype)sharedManager {
|
||
static GiftComboManager *sharedInstance = nil;
|
||
static dispatch_once_t onceToken;
|
||
dispatch_once(&onceToken, ^{
|
||
sharedInstance = [[self alloc] init];
|
||
sharedInstance.giftComboQueue = [NSMutableArray array];
|
||
sharedInstance.activeViews = [NSMutableArray array];
|
||
sharedInstance.comboFlagQueue = [NSMutableArray array];
|
||
sharedInstance.requestQueue = [NSMutableArray array];
|
||
[sharedInstance startProcessingGiftComboFlagQueue];
|
||
});
|
||
return sharedInstance;
|
||
}
|
||
|
||
//// 添加 GiftReceiveInfoModel 和 metadata 到队列
|
||
- (void)addGiftComboWithInfo:(GiftReceiveInfoModel *)info andMetadata:(NSDictionary *)metadata {
|
||
if (info && metadata) {
|
||
// 将元数据打包成字典并添加到队列
|
||
@synchronized (self) {
|
||
NSDictionary *comboData = @{@"info": info, @"metadata": metadata};
|
||
[self.giftComboQueue addObject:comboData];
|
||
}
|
||
// 启动定时器
|
||
[self startProcessingQueue];
|
||
}
|
||
}
|
||
|
||
- (void)addComboFromNIMAttachment:(AttachmentModel *)attachment {
|
||
if (attachment) {
|
||
// 将元数据打包成字典并添加到队列
|
||
@synchronized (self) {
|
||
[self.giftComboQueue addObject:attachment];
|
||
}
|
||
// 启动定时器
|
||
[self startProcessingQueue];
|
||
}
|
||
}
|
||
|
||
// 开始连击,重置
|
||
- (void)reset {
|
||
NSLog(@"[Combo effect] 🔄 开始连击重置 - combo: %ld -> 1, enableCombo: %@, actionCallback: %@",
|
||
(long)self.combo,
|
||
self.enableCombo ? @"YES" : @"NO",
|
||
self.actionCallback ? @"可用" : @"为空");
|
||
|
||
// 确保连击计数正确重置为 1
|
||
_combo = 1;
|
||
_errorMessage = @"";
|
||
|
||
// 验证重置后的状态
|
||
NSLog(@"[Combo effect] 🔍 重置后验证 - combo: %ld", (long)self.combo);
|
||
|
||
// 发送通知,让 GiftComboView 重置显示
|
||
[[NSNotificationCenter defaultCenter] postNotificationName:@"ComboCountReset" object:nil];
|
||
|
||
// 检查是否应该显示连击面板
|
||
if (self.actionCallback && self.enableCombo) {
|
||
NSLog(@"[Combo effect] 📱 触发连击面板显示回调");
|
||
self.actionCallback(ComboAction_ShowPanel);
|
||
self.isCombing = YES;
|
||
} else if (self.actionCallback && !self.enableCombo) {
|
||
NSLog(@"[Combo effect] ⚠️ enableCombo为NO,不显示连击面板");
|
||
} else if (!self.actionCallback) {
|
||
NSLog(@"[Combo effect] ⚠️ actionCallback为空,不显示连击面板");
|
||
}
|
||
|
||
if (self.handleRoomUIChanged) {
|
||
NSLog(@"[Combo effect] 🎮 隐藏房间UI元素");
|
||
self.handleRoomUIChanged(YES);
|
||
}
|
||
NSLog(@"[Combo effect] ✅ 连击重置完成 - isCombing: %@", self.isCombing ? @"YES" : @"NO");
|
||
}
|
||
|
||
- (void)registerActions:(void (^)(ComboActionType))action {
|
||
_combo = 1;
|
||
_errorMessage = @"";
|
||
self.actionCallback = action;
|
||
}
|
||
|
||
// 新增:实现其他简化接口方法
|
||
- (void)activate {
|
||
NSLog(@"[Combo effect] 🔧 激活连击功能");
|
||
self.enableCombo = YES;
|
||
}
|
||
|
||
- (void)deactivate {
|
||
NSLog(@"[Combo effect] 🔧 停用连击功能");
|
||
self.enableCombo = NO;
|
||
}
|
||
|
||
- (BOOL)isActive {
|
||
return self.isCombing && self.enableCombo;
|
||
}
|
||
|
||
- (NSInteger)currentCount {
|
||
// 确保连击计数最少为 1
|
||
if (self.combo < 1) {
|
||
NSLog(@"[Combo effect] ⚠️ currentCount: 连击计数异常,重置为1 - 当前: %ld", (long)self.combo);
|
||
self.combo = 1;
|
||
}
|
||
return self.combo;
|
||
}
|
||
|
||
- (void)incrementCount {
|
||
NSLog(@"[Combo effect] 🔢 增加连击计数 - 当前: %ld -> %ld", (long)self.combo, (long)(self.combo + 1));
|
||
self.combo += 1;
|
||
}
|
||
|
||
- (void)clear {
|
||
NSLog(@"[Combo effect] 🗑️ 清除连击状态");
|
||
[self forceBoomStateReset];
|
||
|
||
// 通知UI移除连击面板
|
||
if (self.actionCallback) {
|
||
NSLog(@"[Combo effect] 📱 触发连击面板移除回调");
|
||
self.actionCallback(ComboAction_RemovePanel);
|
||
}
|
||
}
|
||
|
||
- (void)send {
|
||
NSLog(@"[Combo effect] 📤 发送连击礼物");
|
||
[self sendGift];
|
||
}
|
||
|
||
- (NSDictionary *)stateInfo {
|
||
return [self getComboStateInfo];
|
||
}
|
||
|
||
- (BOOL)canStartCombo {
|
||
return self.enableCombo && self.giftInfo != nil && self.sendGiftToUIDs.count > 0;
|
||
}
|
||
|
||
- (void)validateState {
|
||
[self validateAndFixComboCount];
|
||
}
|
||
|
||
- (void)handleError:(NSError *)error {
|
||
NSLog(@"[Combo effect] ❌ 处理错误: %@", error.localizedDescription);
|
||
self.errorMessage = error.localizedDescription;
|
||
}
|
||
|
||
- (NSString *)lastErrorMessage {
|
||
return self.errorMessage ?: @"";
|
||
}
|
||
|
||
- (void)clearError {
|
||
self.errorMessage = @"";
|
||
}
|
||
|
||
|
||
|
||
- (void)forceBoomStateReset {
|
||
NSLog(@"[Combo effect] 🚨 执行强制Boom连击状态重置");
|
||
|
||
// 1. 立即停止所有定时器(无条件停止)
|
||
NSLog(@"[Combo effect] ⏰ 停止所有定时器");
|
||
[self forceStopAllTimers];
|
||
|
||
// 2. 清空所有队列
|
||
NSLog(@"[Combo effect] 🗑️ 清空所有队列");
|
||
[self clearAllQueues];
|
||
|
||
// 3. 重置所有状态标志
|
||
NSLog(@"[Combo effect] 🔄 重置状态标志 - isCombing: %@ -> NO",
|
||
self.isCombing ? @"YES" : @"NO");
|
||
self.isCombing = NO;
|
||
|
||
// 4. 重置combo计数为0
|
||
_combo = 0;
|
||
NSLog(@"[Combo effect] 🔄 combo计数重置为0");
|
||
|
||
// 注意:不重置 enableCombo,保持连击功能可用状态
|
||
// self.enableCombo = NO; // 移除这行,保持连击功能可用
|
||
|
||
// 注意:不清理 actionCallback,保持回调可用,以便重新进入连击状态
|
||
// self.actionCallback = nil; // 移除这行,保持回调可用
|
||
|
||
// 5. 发送通知(优先级最高,通知所有相关组件)
|
||
NSLog(@"[Combo effect] 📢 发送强制重置通知");
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
[[NSNotificationCenter defaultCenter] postNotificationName:kBoomStateForceResetNotification
|
||
object:nil];
|
||
});
|
||
|
||
// 6. 强制恢复UI(确保底部栏和侧栏显示)
|
||
if (self.handleRoomUIChanged) {
|
||
NSLog(@"[Combo effect] 🎮 恢复房间UI元素");
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
self.handleRoomUIChanged(NO);
|
||
});
|
||
}
|
||
NSLog(@"[Combo effect] ✅ 强制重置完成 - enableCombo保持: %@, actionCallback保持: %@",
|
||
self.enableCombo ? @"YES" : @"NO",
|
||
self.actionCallback ? @"可用" : @"为空");
|
||
}
|
||
|
||
// 无条件停止定时器
|
||
- (void)forceStopAllTimers {
|
||
if (self.timer) {
|
||
dispatch_source_cancel(self.timer);
|
||
self.timer = nil; // 立即置空,不等待回调
|
||
}
|
||
|
||
if (self.comboFlagTimer) {
|
||
dispatch_source_cancel(self.comboFlagTimer);
|
||
self.comboFlagTimer = nil;
|
||
}
|
||
}
|
||
|
||
// 清空所有队列
|
||
- (void)clearAllQueues {
|
||
@synchronized (self) {
|
||
[self.requestQueue removeAllObjects];
|
||
[self.giftComboQueue removeAllObjects];
|
||
[self.comboFlagQueue removeAllObjects];
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
- (NSInteger)loadTotalGiftNum {
|
||
return self.combo * self.countModel.giftNumber.integerValue * self.sendGiftToUIDs.count;
|
||
}
|
||
|
||
|
||
|
||
// 新增:检查连击状态是否有效
|
||
- (BOOL)isComboStateValid {
|
||
@synchronized (self) {
|
||
return self.isCombing &&
|
||
self.enableCombo &&
|
||
self.combo > 0 &&
|
||
self.giftInfo != nil &&
|
||
self.sendGiftToUIDs.count > 0;
|
||
}
|
||
}
|
||
|
||
// 新增:获取连击状态信息
|
||
- (NSDictionary *)getComboStateInfo {
|
||
@synchronized (self) {
|
||
return @{
|
||
@"isCombing": @(self.isCombing),
|
||
@"enableCombo": @(self.enableCombo),
|
||
@"combo": @(self.combo),
|
||
@"hasGiftInfo": @(self.giftInfo != nil),
|
||
@"targetCount": @(self.sendGiftToUIDs.count),
|
||
@"errorMessage": self.errorMessage ?: @""
|
||
};
|
||
}
|
||
}
|
||
|
||
// 新增:打印当前连击状态(用于调试)
|
||
- (void)printComboState {
|
||
NSDictionary *stateInfo = [self getComboStateInfo];
|
||
NSLog(@"[Combo effect] 📊 当前连击状态:");
|
||
NSLog(@"[Combo effect] - isCombing: %@", [stateInfo[@"isCombing"] boolValue] ? @"YES" : @"NO");
|
||
NSLog(@"[Combo effect] - enableCombo: %@", [stateInfo[@"enableCombo"] boolValue] ? @"YES" : @"NO");
|
||
NSLog(@"[Combo effect] - combo: %@", stateInfo[@"combo"]);
|
||
NSLog(@"[Combo effect] - hasGiftInfo: %@", [stateInfo[@"hasGiftInfo"] boolValue] ? @"YES" : @"NO");
|
||
NSLog(@"[Combo effect] - targetCount: %@", stateInfo[@"targetCount"]);
|
||
NSLog(@"[Combo effect] - errorMessage: %@", stateInfo[@"errorMessage"]);
|
||
|
||
// 检查并修复连击计数异常
|
||
[self validateAndFixComboCount];
|
||
}
|
||
|
||
// 新增:验证并修复连击计数
|
||
- (void)validateAndFixComboCount {
|
||
@synchronized (self) {
|
||
if (self.combo < 1) {
|
||
NSLog(@"[Combo effect] 🚨 检测到连击计数异常,自动修复 - 当前: %ld -> 1", (long)self.combo);
|
||
self.combo = 1;
|
||
}
|
||
|
||
if (self.combo > 1000) {
|
||
NSLog(@"[Combo effect] 🚨 检测到连击计数异常,自动修复 - 当前: %ld -> 100", (long)self.combo);
|
||
self.combo = 100;
|
||
}
|
||
}
|
||
}
|
||
|
||
#pragma mark - 处理飘屏逻辑
|
||
- (void)receiveGiftInfoForDisplayComboFlags:(GiftReceiveInfoModel *)receiveInfo
|
||
container:(UIView *)container {
|
||
NSLog(@"[Combo effect] 🎪 收到连击飘屏请求 - combo: %ld, giftId: %ld", (long)receiveInfo.comboCount, (long)receiveInfo.gift.giftId);
|
||
self.containerView = container;
|
||
[self.giftComboQueue addObject:receiveInfo];
|
||
NSLog(@"[Combo effect] 📊 连击飘屏队列数量: %ld", (long)self.giftComboQueue.count);
|
||
[self startProcessingGiftComboFlagQueue];
|
||
}
|
||
|
||
- (void)removeComboFlag {
|
||
self.containerView = nil;
|
||
[self stopProcessingQueue];
|
||
[self stopProcessingGiftComboFlagQueue];
|
||
}
|
||
|
||
- (void)startProcessingGiftComboFlagQueue {
|
||
if (self.comboFlagTimer) {
|
||
return;
|
||
}
|
||
|
||
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
|
||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
|
||
|
||
dispatch_source_set_timer(timer,
|
||
DISPATCH_TIME_NOW,
|
||
0.2 * NSEC_PER_SEC,
|
||
0.01 * NSEC_PER_SEC);
|
||
|
||
@kWeakify(self);
|
||
dispatch_source_set_event_handler(timer, ^{
|
||
@kStrongify(self);
|
||
[self processGiftFlagQueue];
|
||
});
|
||
|
||
dispatch_resume(timer);
|
||
self.comboFlagTimer = timer;
|
||
}
|
||
|
||
- (void)stopProcessingGiftComboFlagQueue {
|
||
if (self.comboFlagTimer) {
|
||
dispatch_source_cancel(self.comboFlagTimer);
|
||
|
||
@kWeakify(self);
|
||
dispatch_source_set_cancel_handler(self.comboFlagTimer, ^{
|
||
@kStrongify(self);
|
||
self.comboFlagTimer = nil;
|
||
});
|
||
}
|
||
}
|
||
|
||
- (void)processGiftFlagQueue {
|
||
if (self.giftComboQueue.count == 0) {
|
||
return;
|
||
}
|
||
|
||
GiftReceiveInfoModel *receiveInfo = [self.giftComboQueue firstObject];
|
||
NSLog(@"[Combo effect] 🎪 处理连击飘屏 - combo: %ld, giftId: %ld", (long)receiveInfo.comboCount, (long)receiveInfo.gift.giftId);
|
||
[self.giftComboQueue xpSafeRemoveObjectAtIndex:0];
|
||
NSLog(@"[Combo effect] 📊 移除后连击飘屏队列数量: %ld", (long)self.giftComboQueue.count);
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
[self handleGiftInfo:receiveInfo];
|
||
});
|
||
}
|
||
|
||
- (void)handleGiftInfo:(GiftReceiveInfoModel *)receiveInfo {
|
||
if (receiveInfo.comboCount < 1) {
|
||
// 不正常的数据,不处理
|
||
return;
|
||
}
|
||
if ([self updateExistingViewWithModel:receiveInfo]) {
|
||
// 如果更新了现有视图,就不需要创建新视图
|
||
return;
|
||
}
|
||
|
||
if (self.activeViews.count >= 2) {
|
||
GiftComboFlagView *oldestView = [self.activeViews firstObject];
|
||
[self animateRemoveView:oldestView];
|
||
}
|
||
|
||
CGFloat positionY = kGetScaleWidth(380);
|
||
CGFloat positionX = isMSRTL() ? -self.containerView.bounds.size.width : self.containerView.bounds.size.width;
|
||
GiftComboFlagView *flagView = [[GiftComboFlagView alloc] initWithFrame:CGRectMake(positionX,
|
||
positionY,
|
||
kGetScaleWidth(300),
|
||
50)];
|
||
@kWeakify(self);
|
||
@kWeakify(flagView);
|
||
[flagView setTimerEnd:^{
|
||
@kStrongify(self);
|
||
@kStrongify(flagView);
|
||
[self animateRemoveView:flagView];
|
||
}];
|
||
[flagView updateReceiveInfoModel:receiveInfo animationType:0];
|
||
|
||
[self allCurrentFlagMoveDown];
|
||
|
||
[self.containerView addSubview:flagView];
|
||
[self.activeViews insertObject:flagView atIndex:0];
|
||
|
||
[self animateView:flagView positionY:positionY];
|
||
}
|
||
|
||
- (void)allCurrentFlagMoveDown {
|
||
CGFloat positionY = kGetScaleWidth(380);
|
||
[self.activeViews enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(GiftComboFlagView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
CGRect rect = obj.frame;
|
||
rect.origin.y = positionY + idx * 50;
|
||
obj.frame = rect;
|
||
});
|
||
}];
|
||
}
|
||
|
||
- (void)animateView:(GiftComboFlagView *)flagView positionY:(CGFloat)positionY {
|
||
[UIView animateWithDuration:0.1
|
||
animations:^{
|
||
if (isMSRTL()) {
|
||
// 获取屏幕宽度
|
||
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
|
||
|
||
// 计算新的 x 坐标:屏幕宽度 - 视图宽度 - 右边距
|
||
CGFloat newX = screenWidth - 20 - kGetScaleWidth(300);
|
||
|
||
// 更新 flagView 的 frame,使右边距离屏幕右边 20 像素
|
||
flagView.frame = CGRectMake(newX, positionY, kGetScaleWidth(300), 50);
|
||
} else {
|
||
flagView.frame = CGRectMake(20, positionY, kGetScaleWidth(300), 50);
|
||
}
|
||
} completion:^(BOOL finished) {
|
||
[self allCurrentFlagMoveDown];
|
||
}];
|
||
}
|
||
|
||
- (void)animateRemoveView:(GiftComboFlagView *)flagView {
|
||
[flagView removeFromSuperview];
|
||
if ([self.activeViews containsObject:flagView]) {
|
||
[self.activeViews removeObject:flagView];
|
||
flagView = nil;
|
||
}
|
||
|
||
[self allCurrentFlagMoveDown];
|
||
}
|
||
|
||
// 更新现有 View 的数据,如果存在相同的 ID
|
||
- (BOOL)updateExistingViewWithModel:(GiftReceiveInfoModel *)model {
|
||
NSInteger index = 0;
|
||
for (GiftComboFlagView *existingFlag in self.activeViews) {
|
||
if (existingFlag.superview == nil) {
|
||
continue;
|
||
}
|
||
if ([existingFlag.receiveInfo isEqual:model]) {
|
||
[self updateComboFlag:existingFlag with:model];
|
||
return YES;
|
||
}
|
||
index++;
|
||
}
|
||
|
||
return NO;
|
||
}
|
||
|
||
- (void)updateComboFlag:(GiftComboFlagView *)flagView with:(GiftReceiveInfoModel *)model {
|
||
[flagView updateReceiveInfoModel:model animationType:1];
|
||
}
|
||
|
||
|
||
// MARK: Logic is 连击面板出现后,每点击一次,就触发一次面板最后的请求,请求成功后,构造云信消息体,消息体进入队列并按 0.25s 一次的频率发送消息
|
||
#pragma mark - 管理队列
|
||
|
||
// 开始处理队列
|
||
- (void)startProcessingQueue {
|
||
if (self.timer) {
|
||
return; // 如果定时器已经在运行,直接返回
|
||
}
|
||
|
||
// 创建 GCD 定时器
|
||
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
|
||
|
||
//#if DEBUG
|
||
// dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC, 0.01 * NSEC_PER_SEC);
|
||
//#else
|
||
// 设置定时器时间间隔:每 0.25 秒执行一次
|
||
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 0.25 * NSEC_PER_SEC, 0.01 * NSEC_PER_SEC);
|
||
//#endif
|
||
|
||
|
||
// 定时器触发的事件处理
|
||
dispatch_source_set_event_handler(self.timer, ^{
|
||
[self processRequestQueue];
|
||
[self processGiftComboQueue];
|
||
});
|
||
|
||
// **立即执行一次处理方法**
|
||
[self processRequestQueue];
|
||
[self processGiftComboQueue];
|
||
|
||
// 启动定时器
|
||
dispatch_resume(self.timer);
|
||
}
|
||
|
||
// 停止处理队列
|
||
- (void)stopProcessingQueue {
|
||
if (self.timer) {
|
||
if (self.requestQueue.count == 0 && self.giftComboQueue.count == 0) {
|
||
// 取消定时器
|
||
dispatch_source_cancel(self.timer);
|
||
|
||
// 设置取消回调,在资源完全释放后将 timer 置为 nil
|
||
@kWeakify(self);
|
||
dispatch_source_set_cancel_handler(self.timer, ^{
|
||
@kStrongify(self);
|
||
self.timer = nil;
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理队列中的第一个请求
|
||
- (void)processGiftComboQueue {
|
||
@synchronized (self) {
|
||
if (self.giftComboQueue.count > 0) {
|
||
// 获取并移除队列中的第一个元数据
|
||
AttachmentModel *attachment = [self.giftComboQueue firstObject];
|
||
[self.giftComboQueue xpSafeRemoveObjectAtIndex:0];
|
||
|
||
// 处理逻辑
|
||
[self processGiftComboWith:attachment];
|
||
} else {
|
||
[self stopProcessingQueue];
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理元数据的实际逻辑
|
||
- (void)processGiftComboWith:(AttachmentModel *)info {
|
||
[self sendCustomMessage:info];
|
||
}
|
||
|
||
- (NSString *)loadErrorMessage {
|
||
return self.errorMessage;
|
||
}
|
||
|
||
- (void)processRequestQueue {
|
||
@synchronized (self) {
|
||
if (self.requestQueue.count > 0) {
|
||
// 获取并移除队列中的第一个元数据
|
||
NSDictionary *dic = [self.requestQueue xpSafeObjectAtIndex:0];
|
||
if (dic) {
|
||
[self handleSendGift:dic];
|
||
[self.requestQueue removeObject:dic];
|
||
}
|
||
} else {
|
||
[self stopProcessingQueue];
|
||
}
|
||
}
|
||
}
|
||
|
||
#pragma mark - Gift meta data
|
||
|
||
// 统一配置方法,替代多个save方法
|
||
- (void)configureWithGiftInfo:(GiftInfoModel *)giftInfo
|
||
targetUIDs:(NSArray *)UIDs
|
||
roomUID:(NSString *)roomUID
|
||
sessionID:(NSString *)sessionID
|
||
userInfo:(UserInfoModel *)userInfo
|
||
countModel:(XPGiftCountModel *)countModel
|
||
sourceType:(GiftSourceType)sourceType
|
||
sendType:(RoomSendGiftType)sendType
|
||
giftNum:(NSString *)giftNum {
|
||
|
||
NSLog(@"[Combo effect] 🔧 统一配置连击参数");
|
||
|
||
self.giftInfo = giftInfo;
|
||
self.sendGiftToUIDs = UIDs;
|
||
self.roomUID = roomUID;
|
||
self.sessionID = sessionID;
|
||
self.sendGiftUserInfo = userInfo;
|
||
self.countModel = countModel;
|
||
self.giftSourceType = sourceType;
|
||
self.roomSendGiftType = sendType;
|
||
self.giftNumPerTimes = giftNum;
|
||
|
||
NSLog(@"[Combo effect] ✅ 连击参数配置完成 - giftId: %ld, targetCount: %ld",
|
||
(long)giftInfo.giftId, (long)UIDs.count);
|
||
}
|
||
|
||
- (BOOL)loadEnable {
|
||
return self.enableCombo;
|
||
}
|
||
|
||
#pragma mark - XPGiftPresenter
|
||
|
||
- (void)sendGift {
|
||
NSLog(@"[Combo effect] 🎁 开始发送连击礼物 - combo: %ld, isCombing: %@", (long)self.combo, self.isCombing ? @"YES" : @"NO");
|
||
|
||
NSString *allUIDs = @"";
|
||
for (NSString *item in self.sendGiftToUIDs) {
|
||
if (allUIDs.length > 0) {
|
||
allUIDs = [allUIDs stringByAppendingString:@","];
|
||
}
|
||
allUIDs = [allUIDs stringByAppendingString:item];
|
||
}
|
||
|
||
NSDictionary *dic = @{
|
||
@"targetUids":allUIDs,
|
||
@"giftNum":self.giftNumPerTimes,
|
||
@"sendType":[NSString stringWithFormat:@"%ld", GiftSendType_OnMic],
|
||
@"giftId":[NSString stringWithFormat:@"%ld", self.giftInfo.giftId],
|
||
@"giftSource":[NSString stringWithFormat:@"%ld", self.giftSourceType],
|
||
@"giftType":[NSString stringWithFormat:@"%ld", self.roomSendGiftType],
|
||
@"roomUid":self.roomUID
|
||
};
|
||
|
||
NSLog(@"[Combo effect] 📦 添加礼物请求到队列 - giftId: %ld, targetUids: %@", (long)self.giftInfo.giftId, allUIDs);
|
||
[self.requestQueue addObject:dic];
|
||
[self startProcessingQueue];
|
||
}
|
||
|
||
- (void)handleSendGift:(NSDictionary *)dic {
|
||
NSString *allUIDs = [dic objectForKey:@"targetUids"];
|
||
NSString *giftId = [dic objectForKey:@"giftId"];
|
||
NSLog(@"[Combo effect] 🌐 开始调用送礼API - giftId: %@, targetUids: %@", giftId, allUIDs);
|
||
|
||
@kWeakify(self);
|
||
[Api requestSendGift:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
|
||
@kStrongify(self);
|
||
if (!self) {
|
||
NSLog(@"[Combo effect] ⚠️ self已释放,忽略API回调");
|
||
return;
|
||
}
|
||
if (code == 200) {
|
||
NSLog(@"[Combo effect] ✅ 送礼API成功 - giftId: %@, combo: %ld", giftId, (long)self.combo);
|
||
GiftReceiveInfoModel *receive = [GiftReceiveInfoModel modelWithJSON:data.data];
|
||
receive.sourceType = [[dic objectForKey:@"giftSource"] integerValue];
|
||
receive.roomSendGiftType = [[dic objectForKey:@"giftType"] integerValue];
|
||
NSArray *array = [allUIDs componentsSeparatedByString:@","];
|
||
receive.receiveGiftNumberUser = array.count;
|
||
[self handleSendGiftSuccess:receive sourceData:data];
|
||
[[NSNotificationCenter defaultCenter] postNotificationName:@"receiveLuckGiftWinning"
|
||
object:@{@"CurrentGold": receive.userPurse.diamonds,
|
||
@"Price": @(receive.gift.goldPrice * receive.giftNum * array.count),
|
||
@"isFromWinning":@(NO)}];
|
||
} else {
|
||
NSLog(@"[Combo effect] ❌ 送礼API失败 - code: %ld, msg: %@", (long)code, msg);
|
||
// 区分错误类型,优化恢复策略
|
||
if (code > 500 && code < 600) {
|
||
// 服务器错误,可能是临时问题,保持连击状态
|
||
NSLog(@"[Combo effect] 🔄 服务器错误,保持连击状态 - code: %ld", (long)code);
|
||
#if DEBUG
|
||
self.errorMessage = [NSString stringWithFormat:@"服务器繁忙,请稍后重试 - %@", msg];
|
||
#else
|
||
self.errorMessage = @"Over Heat & try later";
|
||
dispatch_async(dispatch_get_global_queue(0, 0), ^{
|
||
NSMutableDictionary *logDic = [@{@"targetUids": allUIDs,
|
||
@"giftNum": self.giftNumPerTimes} mutableCopy];
|
||
[logDic addEntriesFromDictionary:dic];
|
||
[logDic setObject:[NSThread callStackSymbols] forKey:@"call stack symbols"];
|
||
[logDic setObject:msg forKey:@"error message"];
|
||
[logDic setObject:@"gift/sendV5" forKey:@"http method"];
|
||
[Bugly reportError:[NSError errorWithDomain:[NSString stringWithFormat:@"UID: %@,API: %@ 异常",
|
||
[AccountInfoStorage instance].getUid,
|
||
@"gift/sendV5"]
|
||
code:code
|
||
userInfo:logDic]];
|
||
});
|
||
#endif
|
||
// 临时错误,不重置连击状态,允许用户重试
|
||
} else if (code == 31005) {
|
||
// 余额不足,需要重置连击状态
|
||
NSLog(@"[Combo effect] 💰 余额不足,强制移除连击状态");
|
||
self.errorMessage = YMLocalizedString(@"XPCandyTreeInsufficientBalanceView1");
|
||
[self clear];
|
||
} else if (code == 8535) {
|
||
// VIP等级不足,需要重置连击状态, 但不可能出现
|
||
NSLog(@"[Combo effect] 👑 VIP等级不足,强制移除连击状态");
|
||
self.errorMessage = @"";
|
||
[self clear];
|
||
} else {
|
||
// 其他错误,根据错误类型决定是否重置连击状态
|
||
self.errorMessage = msg;
|
||
// 对于网络错误等临时问题,保持连击状态
|
||
if (code >= 1000 && code < 2000) {
|
||
// 网络相关错误,保持连击状态
|
||
NSLog(@"[Combo effect] 🌐 网络错误,保持连击状态 - code: %ld", (long)code);
|
||
} else {
|
||
// 其他错误,重置连击状态
|
||
NSLog(@"[Combo effect] 🚨 其他错误,强制移除连击状态 - code: %ld", (long)code);
|
||
[self clear];
|
||
}
|
||
}
|
||
|
||
if (self.actionCallback) {
|
||
self.actionCallback(ComboAction_Error);
|
||
}
|
||
}
|
||
}
|
||
targetUids:allUIDs
|
||
giftNum:self.giftNumPerTimes
|
||
sendType:[dic objectForKey:@"sendType"]
|
||
giftId:[dic objectForKey:@"giftId"]
|
||
giftSource:[dic objectForKey:@"giftSource"]
|
||
giftType:[dic objectForKey:@"giftType"]
|
||
roomUid:[dic objectForKey:@"roomUid"]
|
||
msg:@""
|
||
uid:[AccountInfoStorage instance].getUid];
|
||
}
|
||
|
||
- (void)handleSendGiftSuccess:(GiftReceiveInfoModel *)receive
|
||
sourceData:(BaseModel *)response {
|
||
NSLog(@"[Combo effect] 🎉 连击礼物发送成功 - 当前combo: %ld", (long)self.combo);
|
||
|
||
// 验证连击计数有效性
|
||
[self validateAndFixComboCount];
|
||
|
||
// 在API成功时递增combo计数
|
||
if (self.isCombing) {
|
||
NSLog(@"[Combo effect] 🔢 API成功,递增连击计数 - 当前: %ld -> %ld", (long)self.combo, (long)(self.combo + 1));
|
||
self.combo += 1;
|
||
|
||
// 更新UI显示
|
||
if (self.actionCallback) {
|
||
self.actionCallback(ComboAction_Combo_Count_Update);
|
||
}
|
||
}
|
||
|
||
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithDictionary:response.data];
|
||
|
||
// 确保连击计数最少为 1
|
||
NSInteger comboToSet = self.combo;
|
||
if (comboToSet < 1) {
|
||
NSLog(@"[Combo effect] 🚨 发送云信消息时连击计数异常,修复为 1 - 当前: %ld", (long)comboToSet);
|
||
comboToSet = 1;
|
||
}
|
||
|
||
[dic setObject:@(comboToSet) forKey:@"comboCount"];
|
||
|
||
// 验证连击计数设置
|
||
NSNumber *setComboCount = dic[@"comboCount"];
|
||
NSLog(@"[Combo effect] 🔍 连击计数设置验证 - 设置值: %@, 当前combo: %ld", setComboCount, (long)self.combo);
|
||
|
||
self.sendGiftReceiveInfo = receive;
|
||
|
||
if (self.handleComboSuccess) {
|
||
NSLog(@"[Combo effect] 📨 调用连击成功回调,发送云信消息");
|
||
self.handleComboSuccess(receive, dic);
|
||
}
|
||
|
||
if (self.actionCallback) {
|
||
self.actionCallback(ComboAction_Update_After_Send_Success);
|
||
}
|
||
|
||
NSLog(@"[Combo effect] ✅ 连击礼物处理完成");
|
||
}
|
||
|
||
- (void)sendCustomMessage:(AttachmentModel *)attachment {
|
||
NSLog(@"[Combo effect] 📨 发送云信自定义消息 - combo: %ld", (long)self.combo);
|
||
|
||
NIMMessage *message = [[NIMMessage alloc]init];
|
||
NIMCustomObject *object = [[NIMCustomObject alloc] init];
|
||
object.attachment = attachment;
|
||
message.messageObject = object;
|
||
|
||
UserInfoModel *userInfo = self.sendGiftUserInfo;
|
||
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;
|
||
|
||
//构造会话
|
||
NIMSession *session = [NIMSession session:self.sessionID type:NIMSessionTypeChatroom];
|
||
NSError *error = nil;
|
||
[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error];
|
||
|
||
if (error) {
|
||
NSLog(@"[Combo effect] ❌ 云信消息发送失败 - error: %@", error.localizedDescription);
|
||
} else {
|
||
NSLog(@"[Combo effect] ✅ 云信消息发送成功");
|
||
}
|
||
}
|
||
|
||
|
||
@end
|