Files
peko-ios/YuMi/Modules/YMWeb/MSRoomGameWebVC.m

650 lines
24 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// MSRoomGameWebVC.m
// YuMi
//
// Created by duoban on 2024/4/28.
//
#import "MSRoomGameWebVC.h"
#import "LittleGameInfoModel.h"
#import "RoomInfoModel.h"
#import "XPIAPRechargeViewController.h"
@interface MSWeakWebViewScriptMessageDelegate : NSObject<WKScriptMessageHandler>
//WKScriptMessageHandler 这个协议类专门用来处理JavaScript调用原生OC的方法
@property (nonatomic, weak) id<WKScriptMessageHandler> scriptDelegate;
- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate;
@end
@implementation MSWeakWebViewScriptMessageDelegate
- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate {
self = [super init];
if (self) {
_scriptDelegate = scriptDelegate;
}
return self;
}
//遵循WKScriptMessageHandler协议必须实现如下方法然后把方法向外传递
//通过接收JS传出消息的name进行捕捉的回调方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if ([self.scriptDelegate respondsToSelector:@selector(userContentController:didReceiveScriptMessage:)]) {
[self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
}
}
@end
typedef NS_ENUM(NSInteger, MSGameType) {
MSGameTypeBaiShun, // 百顺游戏
MSGameTypeJoyPlay, // JoyPlay游戏
MSGameTypeCC // CC游戏
};
NSString * const kMSGetConfig = @"getConfig";
NSString * const kMSDestroy = @"destroy";
NSString * const kMSGameRecharge = @"gameRecharge";
NSString * const kMSGameLoaded = @"gameLoaded";
NSString * const kJPRecharge = @"recharge";
NSString * const kJPClickRecharge = @"clickRecharge";
NSString * const kJPClose = @"newTppClose";
@interface MSRoomGameWebVC () <WKNavigationDelegate, WKScriptMessageHandler,WKUIDelegate>
@property (strong, nonatomic) WKWebView *webView;
@property (strong, nonatomic) UIProgressView *progressView;
@property (nonatomic, strong) WKUserContentController *ms_userContentController;
@property (nonatomic,weak) id<RoomHostDelegate> hostDelegate;
@property(nonatomic,strong) ActivityInfoModel *gameModel;
@property(nonatomic,strong) UIButton *backBtn;
@property (nonatomic, assign) MSGameType gameType;
@property (nonatomic, assign) BOOL isCharing;
@property (nonatomic, strong) NSURLRequest *lastRequest;
@property (nonatomic, assign) NSInteger retryCount;
@property (nonatomic, strong) NSTimer *loadingTimer;
@end
@implementation MSRoomGameWebVC
- (void)dealloc {
[self cleanupWebView];
[self hideHUD];
[self.loadingTimer invalidate];
self.loadingTimer = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)cleanupWebView {
if (_webView) {
[_webView stopLoading];
_webView.navigationDelegate = nil;
_webView.UIDelegate = nil;
[_webView removeFromSuperview];
_webView = nil;
}
if (_ms_userContentController) {
[_ms_userContentController removeScriptMessageHandlerForName:kMSGetConfig];
[_ms_userContentController removeScriptMessageHandlerForName:kMSDestroy];
[_ms_userContentController removeScriptMessageHandlerForName:kMSGameRecharge];
[_ms_userContentController removeScriptMessageHandlerForName:kMSGameLoaded];
[_ms_userContentController removeScriptMessageHandlerForName:@"closeGame"];
[_ms_userContentController removeScriptMessageHandlerForName:@"pay"];
[_ms_userContentController removeScriptMessageHandlerForName:kJPClose];
[_ms_userContentController removeScriptMessageHandlerForName:kJPRecharge];
[_ms_userContentController removeScriptMessageHandlerForName:kJPClickRecharge];
_ms_userContentController = nil;
}
}
- (instancetype)initWithDelegate:(id<RoomHostDelegate>)delegate gameModel:(ActivityInfoModel *)gameModel
{
self = [super init];
if (self) {
self.hostDelegate = delegate;
self.gameModel = gameModel;
NSString *upperCode = gameModel.code.uppercaseString;
if ([upperCode isEqualToString:@"BAISHUN"]) {
self.gameType = MSGameTypeBaiShun;
} else if ([upperCode isEqualToString:@"JOYPLAY"]) {
self.gameType = MSGameTypeJoyPlay;
} else {
self.gameType = MSGameTypeCC;
}
}
return self;
}
- (BOOL)isHiddenNavBar {
return YES;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self installUI];
[self setupBackButton];
#if DEBUG
// [self setupLoadingTimer];
#endif
[self setupNotifications];
[self showLoading];
}
- (void)setupLoadingTimer {
self.loadingTimer = [NSTimer scheduledTimerWithTimeInterval:15.0
target:self
selector:@selector(handleLoadingTimeout)
userInfo:nil
repeats:NO];
}
- (void)setupNotifications {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleApplicationDidBecomeActive)
name:UIApplicationDidBecomeActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleApplicationWillResignActive)
name:UIApplicationWillResignActiveNotification
object:nil];
}
- (void)handleLoadingTimeout {
[self hideHUD];
[XNDJTDDLoadingTool showErrorWithMessage:@"加载超时,请检查网络后重试"];
}
- (void)handleApplicationDidBecomeActive {
if (self.webView && !self.webView.isLoading) {
[self.webView reload];
}
}
- (void)handleApplicationWillResignActive {
[self.webView evaluateJavaScript:@"if(typeof(onAppPause) === 'function') { onAppPause(); }" completionHandler:nil];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (self.gameType != MSGameTypeBaiShun &&
self.isCharing) {
self.isCharing = NO;
[self updateCoin];
}
}
- (void)setupBackButton {
if (self.gameType == MSGameTypeJoyPlay && self.gameModel.showType == ActivityShowType_Full) {
return;
}
self.backBtn = [UIButton new];
[self.view addSubview:self.backBtn];
self.backBtn.frame = CGRectMake(0, 0, KScreenWidth, KScreenHeight);
[self.backBtn addTarget:self action:@selector(backBtnAction) forControlEvents:UIControlEventTouchUpInside];
}
- (void)installUI {
self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:0.4];
[self setupWebViewConfiguration];
[self setupWebViewConfiguration];
[self loadGameContent];
}
- (void)setupWebViewConfiguration {
MSWeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[MSWeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];
_ms_userContentController = [[WKUserContentController alloc] init];
[_ms_userContentController addScriptMessageHandler:weakScriptMessageDelegate name:kMSGetConfig];
[_ms_userContentController addScriptMessageHandler:weakScriptMessageDelegate name:kMSDestroy];
[_ms_userContentController addScriptMessageHandler:weakScriptMessageDelegate name:kMSGameRecharge];
[_ms_userContentController addScriptMessageHandler:weakScriptMessageDelegate name:kMSGameLoaded];
/// LEADERCC
[_ms_userContentController addScriptMessageHandler:weakScriptMessageDelegate name:@"closeGame"];
[_ms_userContentController addScriptMessageHandler:weakScriptMessageDelegate name:@"pay"];
/// JOYPLAY
[_ms_userContentController addScriptMessageHandler:weakScriptMessageDelegate
name:kJPClose];
[_ms_userContentController addScriptMessageHandler:weakScriptMessageDelegate
name:kJPRecharge];
[_ms_userContentController addScriptMessageHandler:weakScriptMessageDelegate
name:kJPClickRecharge];
WKWebViewConfiguration *config = [WKWebViewConfiguration new];
config.allowsInlineMediaPlayback = YES;
config.mediaTypesRequiringUserActionForPlayback = NO;
[config setValue:@YES forKey:@"allowUniversalAccessFromFileURLs"];
//⾳视频的播放不需要⽤⼾⼿势触发, 即为⾃动播放
config.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;
config.preferences = [[WKPreferences alloc]init];
WKPreferences *preferences = [WKPreferences new];
preferences.javaScriptCanOpenWindowsAutomatically = YES;
config.preferences = preferences;
config.preferences.javaScriptEnabled = YES;
config.userContentController = _ms_userContentController;
CGRect frame = CGRectZero;
if (self.gameType == MSGameTypeBaiShun) {
frame = CGRectMake(0,0,
KScreenWidth, KScreenHeight);
} else {
if(self.gameModel.showType == ActivityShowType_Half){
frame = CGRectMake(0, KScreenHeight * 0.3,
KScreenWidth, KScreenHeight * 0.7);
} else {
frame = CGRectMake(0,0,
KScreenWidth, KScreenHeight);
}
}
self.webView = [[WKWebView alloc] initWithFrame:frame
configuration:config];
[self.webView.scrollView setBackgroundColor:[UIColor clearColor]];
[self.webView setBackgroundColor:[UIColor clearColor]];
[self.webView setUIDelegate:self];
self.webView.navigationDelegate = self;
//设置⽹⻚透明
[self.webView setOpaque:NO];
//设置⽹⻚全屏
if (@available(iOS 11.0, *)) {
self.webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
[self.view addSubview:self.webView];
}
- (void)loadGameContent {
NSString *h5Url = self.gameModel.skipContent;
NSURL *url = [self handleGameURL:h5Url];
if (!url) {
[self hideHUD];
[XNDJTDDLoadingTool showErrorWithMessage:@"游戏链接无效"];
return;
}
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:15.0];
self.lastRequest = request;
[self.webView loadRequest:request];
}
- (BOOL)isValidGameURL:(NSString *)urlString {
if ([NSString isEmpty:urlString]) {
return NO;
}
NSURL *url = [NSURL URLWithString:urlString];
return (url && url.scheme && url.host);
}
- (NSURL *)handleGameURL:(NSString *)url {
if (![self isValidGameURL:url]) {
return nil;
}
switch (self.gameType) {
case MSGameTypeBaiShun:
url = [self addSupportLanguage:url];
return [NSURL URLWithString:url];
case MSGameTypeJoyPlay:
url = [url stringByAppendingFormat:@"?gameId=%@", @(self.gameModel.gameModel.gameId)];
url = [url stringByAppendingFormat:@"&appKey=%@",
self.gameModel.gameModel.appKey];
url = [url stringByAppendingFormat:@"&mini=%@", @(self.gameModel.showType == ActivityShowType_Half ? 1 : 0)];
if (self.gameModel.showType == ActivityShowType_Full) {
url = [url stringByAppendingFormat:@"&safeTop=1"];
}
url = [url stringByAppendingFormat:@"&roomId=%ld",
(long)self.hostDelegate.getRoomInfo.uid];
break;
case MSGameTypeCC:
url = [url stringByAppendingFormat:@"&roomid=%ld",
(long)self.hostDelegate.getRoomInfo.uid];
break;
default:
break;
}
url = [self addSupportLanguage:url];
if (![NSString isEmpty:self.gameModel.gameModel.urlParam]) {
url = [url stringByAppendingString:[NSString stringWithFormat:@"&%@", self.gameModel.gameModel.urlParam]];
}
url = [url stringByAppendingFormat:@"&token=%@", self.gameModel.gameModel.code];
url = [url stringByAppendingFormat:@"&uid=%@", [AccountInfoStorage instance].getUid];
return [NSURL URLWithString:url];
}
- (NSString *)addSupportLanguage:(NSString *)url {
NSString *mark = @"&";
NSString *lang = @"en-US";
if (![url containsString:@"?"]) {
mark = @"?";
}
if (isMSTR()) {
lang = @"tr-TR";
} else if (isMSZH()) {
lang = @"zh-TW";
} else if (isMSRTL()) {
lang = @"ar-EG";
} else if (isMSPT()) {
lang = @"pt-BR";
} else if (isMSES()) {
lang = @"es-ES";
}
url = [url stringByAppendingFormat:@"%@lang=%@", mark, lang];
return url;
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
[self hideHUD];
self.retryCount = 0;
[self.loadingTimer invalidate];
self.loadingTimer = nil;
if (self.gameType != MSGameTypeBaiShun) {
self.backBtn.frame = CGRectMake(0, 0, KScreenWidth, KScreenHeight*0.3);
}
// 注入错误处理脚本
NSString *errorHandler = @"window.onerror = function(msg, url, line, col, error) { window.webkit.messageHandlers.error.postMessage({message: msg, url: url, line: line, col: col}); return false; }";
[webView evaluateJavaScript:errorHandler completionHandler:nil];
#if DEBUG
NSString *fileName = @"vconsole.min.js"; // 你要查找的文件名
NSString *directory = [[NSBundle mainBundle] resourcePath];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtPath:directory];
NSString *filePath;
for (NSString *path in enumerator) {
if ([[path lastPathComponent] containsString:fileName]) {
filePath = [directory stringByAppendingPathComponent:path];
break;
}
}
if (filePath.length > 0) {
NSString *vConsoleScript = [NSString stringWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding error:nil];
[self.webView evaluateJavaScript:vConsoleScript completionHandler:nil];
// 初始化 vConsole
NSString *initVConsoleScript = @"var vConsole = new VConsole();";
[self.webView evaluateJavaScript:initVConsoleScript completionHandler:nil];
}
#endif
}
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
[self handleWebViewError:error];
}
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
[self handleWebViewError:error];
}
- (void)handleWebViewError:(NSError *)error {
if (self.retryCount < 3) {
self.retryCount++;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.webView loadRequest:self.lastRequest];
});
} else {
[self hideHUD];
[XNDJTDDLoadingTool showErrorWithMessage:@"网络连接失败,请稍后重试"];
}
}
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSURLCredential *card = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential, card);
} else {
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
}
}
// 调⽤JS
- (void)callJs:(NSString*)method withJavaScriptValue:(nullable id)arguments
{
@kWeakify(self);
if (arguments) {
NSData *data = [NSJSONSerialization dataWithJSONObject:arguments
options:(NSJSONWritingPrettyPrinted) error:nil];
NSString *jsonStr = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSString *jsMethods = [NSString stringWithFormat:@"%@(%@)", method,
jsonStr];
[self.webView evaluateJavaScript:jsMethods completionHandler:^(id
_Nullable resp, NSError * _Nullable error) {
@kStrongify(self);
NSLog(@"error = %@ , response = %@",error, resp);
[self.backBtn removeFromSuperview];
}];
} else {
NSString *jsMethods = [NSString stringWithFormat:@"%@({})", method];
[self.webView evaluateJavaScript:jsMethods completionHandler:^(id
_Nullable resp, NSError * _Nullable error) {
@kStrongify(self);
NSLog(@"error = %@ , response = %@",error, resp);
[self.backBtn removeFromSuperview];
}];
}
}
- (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString
{
if (jsonString == nil && jsonString.length == 0) {
return nil;
}
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *err;
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers
error:&err];
if(err)
{
NSLog(@"json解析失败%@",err);
return nil;
}
return dic;
}
// 绑定协议⽅法
- (void)userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message
{
/// BaiShun
switch (self.gameType) {
case MSGameTypeBaiShun: {
NSDictionary *dicBody = [self dictionaryWithJsonString:message.body];
if ([message.name isEqualToString:kMSGetConfig]) {
[self getConfig:dicBody];
} else if ([message.name isEqualToString:kMSDestroy]) {
[self destroy:dicBody];
} else if ([message.name isEqualToString:kMSGameLoaded]) {
[self gameLoaded:dicBody];
} else if ([message.name isEqualToString:kMSGameRecharge]) {
[self gameRecharge:dicBody];
} else {
NSLog(@"未实现⽅法 : %@ --> %@", message.name, message.body);
}
break;
}
case MSGameTypeJoyPlay: {
if ([message.name isEqualToString:kJPRecharge]) {
XPIAPRechargeViewController * webVC =[[XPIAPRechargeViewController alloc] init];
webVC.type = @"4";
[self.navigationController pushViewController:webVC animated:YES];
self.isCharing = YES;
} else if ([message.name isEqualToString:kJPClickRecharge]) {
[self gameRecharge:@{}];
} else if ([message.name isEqualToString:kJPClose]) {
[self backBtnAction];
}
break;
}
case MSGameTypeCC: {
/// LeaderCC
NSString* method = [NSString stringWithFormat:@"%@:", message.name];
SEL selector = NSSelectorFromString(method);
if([self respondsToSelector:selector]){
[self performSelector:selector withObject:message.body];
}else{
NSLog(@"未實現方法 : %@ --> %@", message.name, message.body);
}
break;
}
}
}
#pragma mark - LeaderCC Delegate
// 关闭游戏
- (void)closeGame:(NSDictionary*)args {
[self backBtnAction];
}
// 显示充值界面
- (void)pay:(NSDictionary*)args {
//拉起充值商城
TTAlertConfig *config = [[TTAlertConfig alloc]init];
config.title = YMLocalizedString(@"UserDetail_CP_Toast_0");
config.message = YMLocalizedString(@"XPNobleCenterViewController3");
config.actionStyle = TTAlertActionBothStyle;
@kWeakify(self);
[TTPopup alertWithConfig:config showBorder:NO confirmHandler:^{
@kStrongify(self);
XPIAPRechargeViewController * webVC =[[XPIAPRechargeViewController alloc] init];
webVC.type = @"4";
[self.navigationController pushViewController:webVC animated:YES];
self.isCharing = YES;
} cancelHandler:^{
}];
}
- (void)updateCoin {
// 平时游戏币更新不需要调用该方法,以防止影响游戏开奖动画。
[self.webView evaluateJavaScript:@"updateCoin()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
if (error) {
NSLog(@"Error calling JS function: %@", error.localizedDescription);
} else {
NSLog(@"JS function called successfully, result: %@", result);
}
}];
}
#pragma mark - BaiShun Delegate
// 获取信息配置
- (void) getConfig:(NSDictionary*)args
{
NSLog(@"BSGAME %s","游戏调⽤getConfig");
NSString* method = [args objectForKey:@"jsCallback"];
RoomInfoModel *roomInfo = self.hostDelegate.getRoomInfo;
ActivityInfoItemModel *gameModel = self.gameModel.gameModel;
NSString *appChannel = gameModel.appChannel ?: @"";
int64_t appId = gameModel.appId;
NSString *userId = [AccountInfoStorage instance].getUid;
NSString *code = gameModel.code ?: @"";
NSString *roomId = [NSString stringWithFormat:@"%ld",roomInfo.uid];
NSString *gameMode = gameModel.gameMode ?: @"";
NSDictionary *gameConfig = gameModel.gameConfig ?: @{};
int gsp = gameModel.gsp;
// 数据只是参考值需要根据⾃⼰APP进⾏赋值
NSObject* configData = @{
@"appChannel":appChannel,
@"appId":@(appId),
@"userId":userId,
@"code":code,
@"roomId":roomId,
@"gameMode":gameMode,
@"language":[self getlanguage],
@"gameConfig":gameConfig,
@"gsp":@(gsp),
};
[self callJs:method withJavaScriptValue:configData];
}
-(NSString *)getlanguage{
NSString *language = [NSBundle getLanguageText];
if ([language hasPrefix:@"zh"]) {
if ([language rangeOfString:@"Hans"].location != NSNotFound) {
language = @"0"; // 简体中文
} else {
language = @"1"; // 繁體中文
}
}else if([language hasPrefix:@"ar"]){///阿拉伯语
language = @"7";
}else{///英文
language = @"2";
}
return language;
}
// 销毁游戏
- (void) destroy:(NSDictionary*)args
{
NSLog(@"BSGAME %s","游戏调⽤destroy");
//关闭游戏
[self willMoveToParentViewController:nil]; //1
[self.view removeFromSuperview]; //2
[self removeFromParentViewController]; //3
}
-(void)backBtnAction{
TTAlertConfig *config = [[TTAlertConfig alloc]init];
config.title = YMLocalizedString(@"UserDetail_CP_Toast_0");
config.message = YMLocalizedString(@"MSRoomGameWebVC0");
config.actionStyle = TTAlertActionBothStyle;
@kWeakify(self);
[TTPopup alertWithConfig:config showBorder:NO confirmHandler:^{
@kStrongify(self);
[self destroy:nil];
} cancelHandler:^{
}];
}
// 提⽰余额不⾜
- (void) gameRecharge:(NSDictionary*)args
{
NSLog(@"BSGAME %s","游戏调⽤gameRecharge");
//拉起充值商城
TTAlertConfig *config = [[TTAlertConfig alloc]init];
config.title = YMLocalizedString(@"UserDetail_CP_Toast_0");
config.message = YMLocalizedString(@"XPNobleCenterViewController3");
config.actionStyle = TTAlertActionBothStyle;
@kWeakify(self);
[TTPopup alertWithConfig:config showBorder:NO confirmHandler:^{
@kStrongify(self);
XPIAPRechargeViewController * webVC =[[XPIAPRechargeViewController alloc] init];
webVC.type = @"4";
[self.navigationController pushViewController:webVC animated:YES];
self.isCharing = YES;
} cancelHandler:^{
}];
}
// 游戏加载完毕
- (void) gameLoaded:(NSDictionary*)args
{
// 游戏加载完毕
[self.backBtn removeFromSuperview];
}
@end