From 78b788262c4661eead4d4fa3d3115a15071b99e9 Mon Sep 17 00:00:00 2001 From: khanhduytran0 Date: Sun, 4 Dec 2022 11:41:54 +0700 Subject: [PATCH] Move LoginViewController to parts - The account selector now has Add Account item - Last selected account is kept - Account token refreshment is done on startup and on select if need - Moved JIT status to LauncherMenuViewController's toolbar - Primary View Controller is now LauncherSplitViewController - More unmentioned(?), see diff. --- Natives/AccountListViewController.h | 2 +- Natives/AccountListViewController.m | 196 +++++++- Natives/AppDelegate.m | 7 - .../IconAdd.imageset/Contents.json | 12 + .../Assets.xcassets/IconAdd.imageset/icon.png | Bin 0 -> 181 bytes .../SpinnerArrow.imageset/Contents.json | 12 + .../SpinnerArrow.imageset/icon.png | Bin 0 -> 205 bytes Natives/CMakeLists.txt | 1 - Natives/LauncherMenuViewController.m | 156 ++++++- Natives/LauncherNavigationController.m | 43 +- Natives/LauncherPreferences.m | 14 +- Natives/LauncherSplitViewController.m | 26 ++ Natives/LoginViewController.h | 6 - Natives/LoginViewController.m | 434 ------------------ Natives/MinecraftResourceUtils.m | 1 + Natives/SceneDelegate.m | 8 - Natives/UIKit+hook.m | 11 - Natives/authenticator/BaseAuthenticator.h | 2 +- Natives/authenticator/BaseAuthenticator.m | 11 +- Natives/authenticator/LocalAuthenticator.m | 4 +- .../authenticator/MicrosoftAuthenticator.m | 78 ++-- Natives/en.lproj/Localizable.strings | 13 +- Natives/ios_uikit_bridge.m | 11 +- Natives/utils.m | 14 +- README.md | 2 +- 25 files changed, 495 insertions(+), 569 deletions(-) create mode 100644 Natives/Assets.xcassets/IconAdd.imageset/Contents.json create mode 100644 Natives/Assets.xcassets/IconAdd.imageset/icon.png create mode 100644 Natives/Assets.xcassets/SpinnerArrow.imageset/Contents.json create mode 100644 Natives/Assets.xcassets/SpinnerArrow.imageset/icon.png delete mode 100644 Natives/LoginViewController.h delete mode 100644 Natives/LoginViewController.m diff --git a/Natives/AccountListViewController.h b/Natives/AccountListViewController.h index 4d1f418ad5..76ed6fb7e4 100644 --- a/Natives/AccountListViewController.h +++ b/Natives/AccountListViewController.h @@ -4,6 +4,6 @@ @property (nonatomic, copy) void (^whenDelete)(NSString* name); -@property(nonatomic, copy) void (^whenItemSelected)(NSString* name); +@property(nonatomic, copy) void (^whenItemSelected)(); @end diff --git a/Natives/AccountListViewController.m b/Natives/AccountListViewController.m index 08fcd3ff34..fc1a7c831f 100644 --- a/Natives/AccountListViewController.m +++ b/Natives/AccountListViewController.m @@ -1,7 +1,12 @@ +#import + +#import "authenticator/BaseAuthenticator.h" #import "AccountListViewController.h" #import "AFNetworking.h" -#import "utils.h" +#import "LauncherPreferences.h" #import "UIImageView+AFNetworking.h" +#import "ios_uikit_bridge.h" +#import "utils.h" @interface ATableViewCell : UITableViewCell @end @@ -11,9 +16,10 @@ @implementation ATableViewCell @end -@interface AccountListViewController() +@interface AccountListViewController() @property(nonatomic, strong) NSMutableArray *accountList; +@property(nonatomic) ASWebAuthenticationSession *authVC; @end @@ -46,7 +52,7 @@ - (void)viewDidLoad { - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return self.accountList.count; + return self.accountList.count + 1; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath @@ -57,6 +63,12 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"]; } + if (indexPath.row == self.accountList.count) { + cell.imageView.image = [UIImage imageNamed:@"IconAdd"]; + cell.textLabel.text = localize(@"login.option.add", nil); + return cell; + } + NSDictionary *selected = self.accountList[indexPath.row]; // By default, display the saved username cell.textLabel.text = selected[@"username"]; @@ -78,9 +90,24 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - [self dismissViewControllerAnimated:YES completion:nil]; + [tableView deselectRowAtIndexPath:indexPath animated:NO]; + UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; - self.whenItemSelected(self.accountList[indexPath.row][@"username"]); + if (indexPath.row == self.accountList.count) { + [self actionAddAccount:cell]; + return; + } + + if (@available(iOS 13.0, *)) { + self.modalInPresentation = YES; + } + self.tableView.userInteractionEnabled = NO; + [self addActivityIndicatorTo:cell]; + + id callback = ^(NSString* status, BOOL success) { + [self callbackMicrosoftAuth:status success:success forCell:cell]; + }; + [[BaseAuthenticator loadSavedName:self.accountList[indexPath.row][@"username"]] refreshTokenWithCallback:callback]; } - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { @@ -91,7 +118,7 @@ - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEd NSFileManager *fm = [NSFileManager defaultManager]; NSString *path = [NSString stringWithFormat:@"%s/accounts/%@.json", getenv("POJAV_HOME"), str]; if (self.whenDelete != nil) { - self.whenDelete(path); + self.whenDelete(str); } [fm removeItemAtPath:path error:nil]; [self.accountList removeObjectAtIndex:indexPath.row]; @@ -99,9 +126,166 @@ - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEd } } +- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (indexPath.row == self.accountList.count) { + return UITableViewCellEditingStyleNone; + } else { + return UITableViewCellEditingStyleDelete; + } +} + +- (void)actionAddAccount:(UITableViewCell *)sender { + UIAlertController *picker = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; + UIAlertAction *actionMicrosoft = [UIAlertAction actionWithTitle:localize(@"login.option.microsoft", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [self actionLoginMicrosoft:sender]; + }]; + [picker addAction:actionMicrosoft]; + UIAlertAction *actionLocal = [UIAlertAction actionWithTitle:localize(@"login.option.local", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [self actionLoginLocal:sender]; + }]; + [picker addAction:actionLocal]; + UIAlertAction *cancel = [UIAlertAction actionWithTitle:localize(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil]; + [picker addAction:cancel]; + + picker.popoverPresentationController.sourceView = sender; + picker.popoverPresentationController.sourceRect = sender.bounds; + + [self presentViewController:picker animated:YES completion:nil]; +} + +- (void)actionLoginLocal:(UIView *)sender { + if ([getPreference(@"local_warn") boolValue] == YES) { + setPreference(@"local_warn", @NO); + UIAlertController *alert = [UIAlertController alertControllerWithTitle:localize(@"login.warn.title.localmode", nil) message:localize(@"login.warn.message.localmode", nil) preferredStyle:UIAlertControllerStyleActionSheet]; + alert.popoverPresentationController.sourceView = sender; + alert.popoverPresentationController.sourceRect = sender.bounds; + UIAlertAction *ok = [UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self actionLoginLocal:sender];}]; + [alert addAction:ok]; + [self presentViewController:alert animated:YES completion:nil]; + return; + } + UIAlertController *controller = [UIAlertController alertControllerWithTitle:localize(@"Sign in", nil) message:localize(@"login.option.local", nil) preferredStyle:UIAlertControllerStyleAlert]; + [controller addTextFieldWithConfigurationHandler:^(UITextField *textField) { + textField.placeholder = localize(@"login.alert.field.username", nil); + textField.clearButtonMode = UITextFieldViewModeWhileEditing; + textField.borderStyle = UITextBorderStyleRoundedRect; + }]; + [controller addAction:[UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { + NSArray *textFields = controller.textFields; + UITextField *usernameField = textFields[0]; + if (usernameField.text.length < 3 || usernameField.text.length > 16) { + controller.message = localize(@"login.error.username.outOfRange", nil); + [self presentViewController:controller animated:YES completion:nil]; + } else { + id callback = ^(NSString* status, BOOL success) { + [self dismissViewControllerAnimated:YES completion:nil]; + self.whenItemSelected(); + }; + [[[LocalAuthenticator alloc] initWithInput:usernameField.text] loginWithCallback:callback]; + } + }]]; + [controller addAction:[UIAlertAction actionWithTitle:localize(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil]]; + [self presentViewController:controller animated:YES completion:nil]; +} + +- (void)actionLoginMicrosoft:(UITableViewCell *)sender { + NSURL *url = [NSURL URLWithString:@"https://login.live.com/oauth20_authorize.srf?client_id=00000000402b5328&response_type=code&scope=service%3A%3Auser.auth.xboxlive.com%3A%3AMBI_SSL&redirect_url=https%3A%2F%2Flogin.live.com%2Foauth20_desktop.srf"]; + + self.authVC = + [[ASWebAuthenticationSession alloc] initWithURL:url + callbackURLScheme:@"ms-xal-00000000402b5328" + completionHandler:^(NSURL * _Nullable callbackURL, NSError * _Nullable error) + { + if (callbackURL == nil) { + if (error.code != ASWebAuthenticationSessionErrorCodeCanceledLogin) { + showDialog(self, localize(@"Error", nil), error.localizedDescription); + } + return; + } + NSString *urlString = [callbackURL absoluteString]; + // NSLog(@"URL returned = %@", [callbackURL absoluteString]); + + if ([urlString containsString:@"/auth/?code="]) { + if (@available(iOS 13.0, *)) { + self.modalInPresentation = YES; + } + self.tableView.userInteractionEnabled = NO; + [self addActivityIndicatorTo:sender]; + NSArray *components = [urlString componentsSeparatedByString:@"/auth/?code="]; + id callback = ^(NSString* status, BOOL success) { + [self callbackMicrosoftAuth:status success:success forCell:sender]; + }; + [[[MicrosoftAuthenticator alloc] initWithInput:components[1]] loginWithCallback:callback]; + } else { + NSArray *components = [urlString componentsSeparatedByString:@"/auth/?error="]; + if ([components[1] hasPrefix:@"access_denied"]) { + // Ignore access denial responses + return; + } + NSString *outError = [components[1] + stringByReplacingOccurrencesOfString:@"&error_description=" withString:@": "]; + outError = [outError stringByRemovingPercentEncoding]; + showDialog(self, localize(@"Error", nil), outError); + } + }]; + + if (@available(iOS 13.0, *)) { + self.authVC.prefersEphemeralWebBrowserSession = YES; + self.authVC.presentationContextProvider = self; + } + + if ([self.authVC start] == NO) { + showDialog(self, localize(@"Error", nil), @"Unable to open Safari"); + } +} + +- (void)addActivityIndicatorTo:(UITableViewCell *)cell { + UIActivityIndicatorViewStyle indicatorStyle; + if (@available(iOS 13.0, *)) { + indicatorStyle = UIActivityIndicatorViewStyleMedium; + } else { + indicatorStyle = UIActivityIndicatorViewStyleGray; + } + UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:indicatorStyle]; + cell.accessoryView = indicator; + [indicator sizeToFit]; + [indicator startAnimating]; +} + +- (void)removeActivityIndicatorFrom:(UITableViewCell *)cell { + UIActivityIndicatorView *indicator = (id)cell.accessoryView; + [indicator stopAnimating]; + cell.accessoryView = nil; +} + +- (void)callbackMicrosoftAuth:(NSString *)status success:(BOOL)success forCell:(UITableViewCell *)cell { + if (status != nil) { + cell.detailTextLabel.text = status; + if (!success) { + if (@available(iOS 13.0, *)) { + self.modalInPresentation = NO; + } + self.tableView.userInteractionEnabled = YES; + [self removeActivityIndicatorFrom:cell]; + NSLog(@"[MSA] Error: %@", status); + showDialog(self, localize(@"Error", nil), status); + } + } else if (success) { + [self removeActivityIndicatorFrom:cell]; + [self dismissViewControllerAnimated:YES completion:nil]; + self.whenItemSelected(); + } +} + #pragma mark - UIPopoverPresentationControllerDelegate - (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection { return UIModalPresentationNone; } +#pragma mark - ASWebAuthenticationPresentationContextProviding +- (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session API_AVAILABLE(ios(13.0)){ + return UIApplication.sharedApplication.windows.firstObject; +} + @end diff --git a/Natives/AppDelegate.m b/Natives/AppDelegate.m index 89ac13093b..28145e8211 100644 --- a/Natives/AppDelegate.m +++ b/Natives/AppDelegate.m @@ -1,5 +1,4 @@ #import "AppDelegate.h" -#import "LoginViewController.h" #import "SceneDelegate.h" #import "ios_uikit_bridge.h" #import "utils.h" @@ -36,12 +35,6 @@ - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet< // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } -- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { - NSDictionary *data = [NSDictionary dictionaryWithObject:url forKey:@"url"]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"MSALoginCallback" object:self userInfo:data]; - return YES; -} - - (void)applicationDidBecomeActive:(UIApplication *)application { CallbackBridge_setWindowAttrib(GLFW_FOCUSED, 1); } diff --git a/Natives/Assets.xcassets/IconAdd.imageset/Contents.json b/Natives/Assets.xcassets/IconAdd.imageset/Contents.json new file mode 100644 index 0000000000..1ffad00db4 --- /dev/null +++ b/Natives/Assets.xcassets/IconAdd.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "icon.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Natives/Assets.xcassets/IconAdd.imageset/icon.png b/Natives/Assets.xcassets/IconAdd.imageset/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..703c428710e54b7a123e9531edb7c22f856d89e7 GIT binary patch literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEEX7WqAsj$Z!;#Vf;-ixRFR$ufeiF5V1;j=K8H3OH)Wi zf>$TK{(h(Xno4o#)`$5Q99~^-hR>s#+2AqIUItHBKbLh* G2~7a=20YXLMJ+X0hH+Rj?7vVqH kOP|jF4OPwzH26v}!(kN_y^y4N2Y@^VPgg&ebxsLQ0C2riO#lD@ literal 0 HcmV?d00001 diff --git a/Natives/CMakeLists.txt b/Natives/CMakeLists.txt index 1a1c21450a..a3e5010a3e 100644 --- a/Natives/CMakeLists.txt +++ b/Natives/CMakeLists.txt @@ -124,7 +124,6 @@ add_executable(PojavLauncher LauncherPreferencesViewController.m LauncherPrefGameDirViewController.m LauncherSplitViewController.m - LoginViewController.m MinecraftResourceUtils.m SceneDelegate.m SceneExternalDelegate.m diff --git a/Natives/LauncherMenuViewController.m b/Natives/LauncherMenuViewController.m index 3c6fb60188..577fbf770d 100644 --- a/Natives/LauncherMenuViewController.m +++ b/Natives/LauncherMenuViewController.m @@ -1,10 +1,16 @@ +#import + #import "authenticator/BaseAuthenticator.h" #import "AccountListViewController.h" +#import "AFNetworking.h" +#import "ALTServerConnection.h" #import "LauncherMenuViewController.h" #import "LauncherNewsViewController.h" +#import "LauncherPreferences.h" #import "LauncherPreferencesViewController.h" #import "UIButton+AFNetworking.h" #import "UIImageView+AFNetworking.h" +#import "ios_uikit_bridge.h" #import "utils.h" @implementation LauncherMenuCustomItem @@ -20,9 +26,10 @@ + (LauncherMenuCustomItem *)title:(NSString *)title imageName:(NSString *)imageN @end @interface LauncherMenuViewController() -@property(nonatomic) NSArray *options; +@property(nonatomic) NSMutableArray *options; @property(nonatomic) UIButton *accountButton; @property(nonatomic) UIBarButtonItem *accountBtnItem; +@property(nonatomic) UILabel *statusLabel; @end @implementation LauncherMenuViewController @@ -65,12 +72,43 @@ - (void)viewDidLoad { [self presentViewController:activityVC animated:YES completion:nil]; }] - ]; + ].mutableCopy; self.options[0].title = localize(@"News", nil); self.options[1].title = localize(@"Settings", nil); + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.dateFormat = @"MM-dd"; + NSString* date = [dateFormatter stringFromDate:NSDate.date]; + if([date isEqualToString:@"06-29"] || [date isEqualToString:@"06-30"] || [date isEqualToString:@"07-01"]) { + [self.options addObject:(id)[LauncherMenuCustomItem + title:@"Technoblade never dies!" + imageName:@"" action:^{ + SFSafariViewController *vc = [[SFSafariViewController alloc] initWithURL:[NSURL URLWithString:@"https://youtu.be/DPMluEVUqS0"]]; + [self presentViewController:vc animated:YES completion:nil]; + }]]; + } + self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + self.navigationController.toolbarHidden = NO; + UIActivityIndicatorViewStyle indicatorStyle; + if (@available(iOS 13.0, *)) { + indicatorStyle = UIActivityIndicatorViewStyleMedium; + } else { + indicatorStyle = UIActivityIndicatorViewStyleGray; + } + UIActivityIndicatorView *toolbarIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:indicatorStyle]; + [toolbarIndicator startAnimating]; + self.toolbarItems = @[ + [[UIBarButtonItem alloc] initWithCustomView:toolbarIndicator], + [[UIBarButtonItem alloc] init] + ]; + if (@available(iOS 13.0, *)) { + self.toolbarItems[1].tintColor = UIColor.labelColor; + } else { + self.toolbarItems[1].tintColor = UIColor.blackColor; + } + // Setup the account button self.accountButton = [UIButton buttonWithType:UIButtonTypeCustom]; [self.accountButton addTarget:self action:@selector(selectAccount:) forControlEvents:UIControlEventTouchUpInside]; @@ -85,8 +123,14 @@ - (void)viewDidLoad { [self tableView:self.tableView didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; - // Put a close button, as iOS does not have a dedicated back button - self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"❌" style:UIBarButtonItemStyleDone target:self.splitViewController action:@selector(dismissViewController)]; + if (!getEntitlementValue(@"dynamic-codesigning")) { + if (isJITEnabled()) { + [self displayProgress:localize(@"login.jit.enabled", nil)]; + [self displayProgress:nil]; + } else { + [self enableJITWithJitStreamer]; + } + } } - (void)viewWillAppear:(BOOL)animated { @@ -137,7 +181,6 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath if ([selected isKindOfClass:UIViewController.class]) { [self.splitViewController.viewControllers[1] setViewControllers:@[selected] animated:NO]; //YES? - if (@available(iOS 14.0, tvOS 14.0, *)) { selected.navigationItem.leftBarButtonItem = self.accountBtnItem; // It is unnecessary to put the toggle button as it is automated on iOS 14+ @@ -152,10 +195,20 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath - (void)selectAccount:(UIButton *)sender { AccountListViewController *vc = [[AccountListViewController alloc] init]; - vc.whenItemSelected = ^void(NSString* name) { - // TODO: perform token refreshing - [BaseAuthenticator loadSavedName:name]; + vc.whenDelete = ^void(NSString* name) { + if ([name isEqualToString:getPreference(@"selected_account")]) { + BaseAuthenticator.current = nil; + setPreference(@"selected_account", @""); + [self updateAccountInfo]; + } + }; + vc.whenItemSelected = ^void() { + setPreference(@"selected_account", BaseAuthenticator.current.authData[@"username"]); [self updateAccountInfo]; + if (sender != self.accountButton) { + // Called from the play button, so call back to continue + [sender sendActionsForControlEvents:UIControlEventTouchUpInside]; + } }; vc.modalPresentationStyle = UIModalPresentationPopover; vc.preferredContentSize = CGSizeMake(350, 250); @@ -170,6 +223,14 @@ - (void)selectAccount:(UIButton *)sender { - (void)updateAccountInfo { NSDictionary *selected = BaseAuthenticator.current.authData; + + if (selected == nil) { + [self.accountButton setAttributedTitle:[[NSAttributedString alloc] initWithString:localize(@"login.option.select", nil)] forState:UIControlStateNormal]; + [self.accountButton setImage:[UIImage imageNamed:@"DefaultAccount"] forState:UIControlStateNormal]; + [self.accountButton sizeToFit]; + return; + } + BOOL isDemo = [selected[@"username"] hasPrefix:@"Demo."]; NSMutableAttributedString *title = [[NSMutableAttributedString alloc] initWithString:[selected[@"username"] substringFromIndex:(isDemo?5:0)]]; id subtitle; @@ -194,4 +255,83 @@ - (void)updateAccountInfo { [self.accountButton sizeToFit]; } +- (void)displayProgress:(NSString *)status { + if (status == nil) { + [(UIActivityIndicatorView *)self.toolbarItems[0].customView stopAnimating]; + return; + } + self.toolbarItems[1].title = status; +} + +- (void)enableJITWithAltJIT +{ + [self displayProgress:localize(@"login.jit.start.AltKit", nil)]; + [ALTServerManager.sharedManager startDiscovering]; + [ALTServerManager.sharedManager autoconnectWithCompletionHandler:^(ALTServerConnection *connection, NSError *error) { + if (error) { + NSLog(@"[AltKit] Could not auto-connect to server. %@", error); + [self displayProgress:localize(@"login.jit.fail.AltKit", nil)]; + [self displayProgress:nil]; + return; + } + [connection enableUnsignedCodeExecutionWithCompletionHandler:^(BOOL success, NSError *error) { + if (success) { + NSLog(@"[AltKit] Successfully enabled JIT compilation!"); + [ALTServerManager.sharedManager stopDiscovering]; + [self displayProgress:localize(@"login.jit.enabled", nil)]; + [self displayProgress:nil]; + } else { + NSLog(@"[AltKit] Could not enable JIT compilation. %@", error); + [self displayProgress:localize(@"login.jit.fail.AltKit", nil)]; + [self displayProgress:nil]; + showDialog(self, localize(@"Error", nil), error.description); + } + [connection disconnect]; + }]; + }]; +} + +- (void)enableJITWithJitStreamer +{ + [self displayProgress:localize(@"login.jit.checking", nil)]; + + // TODO: customizable address + NSString *address = getPreference(@"jitstreamer_server"); + NSLog(@"JitStreamer server is %@, attempting to connect...", address); + + AFHTTPSessionManager *manager = AFHTTPSessionManager.manager; + manager.requestSerializer.timeoutInterval = 10; + manager.responseSerializer = AFHTTPResponseSerializer.serializer; + manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/plain", nil]; + [manager GET:[NSString stringWithFormat:@"http://%@/version", address] parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask *task, NSData *response) { + NSString *version = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding]; + NSLog(@"Found JitStreamer %@", version); + [self displayProgress:localize(@"login.jit.found.JitStreamer", nil)]; + manager.requestSerializer.timeoutInterval = 0; + manager.responseSerializer = AFJSONResponseSerializer.serializer; + void(^handleResponse)(NSURLSessionDataTask *task, id response) = ^void(NSURLSessionDataTask *task, id response){ + NSDictionary *responseDict; + // FIXME: successful response may fail due to serialization issues + if ([response isKindOfClass:NSError.class]) { + NSLog(@"Error?: %@", responseDict); + NSData *errorData = ((NSError *)response).userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]; + responseDict = [NSJSONSerialization JSONObjectWithData:errorData options:0 error:nil]; + } else { + responseDict = response; + } + if ([responseDict[@"success"] boolValue]) { + [self displayProgress:localize(@"login.jit.enabled", nil)]; + [self displayProgress:nil]; + } else { + [self displayProgress:[NSString stringWithFormat:localize(@"login.jit.fail.JitStreamer", nil), responseDict[@"message"]]]; + showDialog(self, localize(@"Error", nil), responseDict[@"message"]); + [self enableJITWithAltJIT]; + } + }; + [manager POST:[NSString stringWithFormat:@"http://%@/attach/%d/", address, getpid()] parameters:nil headers:nil progress:nil success:handleResponse failure:handleResponse]; + } failure:^(NSURLSessionDataTask *task, NSError *error) { + [self enableJITWithAltJIT]; + }]; +} + @end diff --git a/Natives/LauncherNavigationController.m b/Natives/LauncherNavigationController.m index b43eb7d855..6f7a7d90a7 100644 --- a/Natives/LauncherNavigationController.m +++ b/Natives/LauncherNavigationController.m @@ -1,3 +1,4 @@ +#import "authenticator/BaseAuthenticator.h" #import "AFNetworking.h" #import "CustomControlsViewController.h" #import "JavaGUIViewController.h" @@ -29,10 +30,13 @@ - (void)viewDidLoad [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; - self.versionTextField = [[UITextField alloc] initWithFrame:CGRectMake(0, 4, self.toolbar.frame.size.width, self.toolbar.frame.size.height/2 - 4)]; + self.versionTextField = [[UITextField alloc] initWithFrame:CGRectMake(0, 4, self.toolbar.frame.size.width * 0.8, self.toolbar.frame.size.height - 8)]; [self.versionTextField addTarget:self.versionTextField action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit]; self.versionTextField.autoresizingMask = AUTORESIZE_MASKS; self.versionTextField.placeholder = @"Specify version..."; + self.versionTextField.rightView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SpinnerArrow"]]; + self.versionTextField.rightView.frame = CGRectMake(0, 0, self.versionTextField.frame.size.height * 0.9, self.versionTextField.frame.size.height * 0.9); + self.versionTextField.rightViewMode = UITextFieldViewModeAlways; self.versionTextField.text = (NSString *) getPreference(@"selected_version"); self.versionTextField.textAlignment = NSTextAlignmentCenter; @@ -62,8 +66,8 @@ - (void)viewDidLoad [self.toolbar addSubview:self.versionTextField]; - self.progressViewMain = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 0, self.toolbar.frame.size.width, 4.0)]; - self.progressViewSub = [[UIProgressView alloc] initWithFrame:CGRectMake(0, self.toolbar.frame.size.height - 4.0, self.toolbar.frame.size.width, 4.0)]; + self.progressViewMain = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 0, self.toolbar.frame.size.width, 4)]; + self.progressViewSub = [[UIProgressView alloc] initWithFrame:CGRectMake(0, self.toolbar.frame.size.height - 4, self.toolbar.frame.size.width, 4)]; self.progressViewMain.autoresizingMask = self.progressViewSub.autoresizingMask = AUTORESIZE_MASKS; self.progressViewMain.hidden = self.progressViewSub.hidden = YES; [self.toolbar addSubview:self.progressViewMain]; @@ -76,18 +80,32 @@ - (void)viewDidLoad self.buttonInstall.autoresizingMask = AUTORESIZE_MASKS; self.buttonInstall.backgroundColor = [UIColor colorWithRed:54/255.0 green:176/255.0 blue:48/255.0 alpha:1.0]; self.buttonInstall.layer.cornerRadius = 5; - self.buttonInstall.frame = CGRectMake(6.0, self.toolbar.frame.size.height/2, self.toolbar.frame.size.width - 12.0, (self.toolbar.frame.size.height - 12.0)/2); + self.buttonInstall.frame = CGRectMake(self.toolbar.frame.size.width * 0.8, 4, self.toolbar.frame.size.width * 0.2, self.toolbar.frame.size.height - 8); self.buttonInstall.tintColor = UIColor.whiteColor; [self.buttonInstall addTarget:self action:@selector(launchMinecraft:) forControlEvents:UIControlEventTouchUpInside]; [self.toolbar addSubview:self.buttonInstall]; - self.progressText = [[UILabel alloc] initWithFrame:self.buttonInstall.frame]; + self.progressText = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.toolbar.frame.size.width, self.toolbar.frame.size.height)]; self.progressText.adjustsFontSizeToFitWidth = YES; self.progressText.autoresizingMask = AUTORESIZE_MASKS; self.progressText.font = [self.progressText.font fontWithSize:16]; self.progressText.textAlignment = NSTextAlignmentCenter; self.progressText.userInteractionEnabled = NO; [self.toolbar addSubview:self.progressText]; + + if ([BaseAuthenticator.current isKindOfClass:MicrosoftAuthenticator.class]) { + // Perform token refreshment on startup + [self setInteractionEnabled:NO]; + id callback = ^(NSString* status, BOOL success) { + self.progressText.text = status; + if (status == nil) { + [self setInteractionEnabled:YES]; + } else if (!success) { + showDialog(self, localize(@"Error", nil), status); + } + }; + [BaseAuthenticator.current refreshTokenWithCallback:callback]; + } } - (BOOL)isVersionInstalled:(NSString *)versionId @@ -223,14 +241,9 @@ - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocum } - (void)setInteractionEnabled:(BOOL)enabled { - // Obtain LauncherMenu's navigation item - UINavigationItem *item = [(UINavigationController *)self.splitViewController.viewControllers[0] - viewControllers][0].navigationItem; - ((UIButton *)item.titleView).enabled = enabled; - item.rightBarButtonItem.enabled = enabled; - for (UIControl *view in self.toolbar.subviews) { if ([view isKindOfClass:UIControl.class]) { + view.alpha = enabled ? 1 : 0.2; view.enabled = enabled; } } @@ -243,6 +256,14 @@ - (void)launchMinecraft:(UIButton *)sender { return; } + if (BaseAuthenticator.current == nil) { + // Present the account selector if none selected + UIViewController *view = [(UINavigationController *)self.splitViewController.viewControllers[0] + viewControllers][0]; + [view performSelector:@selector(selectAccount:) withObject:sender]; + return; + } + sender.alpha = 0.5; [self setInteractionEnabled:NO]; diff --git a/Natives/LauncherPreferences.m b/Natives/LauncherPreferences.m index 040e96034c..ced31b2a79 100644 --- a/Natives/LauncherPreferences.m +++ b/Natives/LauncherPreferences.m @@ -116,6 +116,7 @@ void loadPreferences(BOOL reset) { // set default value setDefaultValueForPref(envPrefDict, @"resolution", @(100)); setDefaultValueForPref(prefDict, @"button_scale", @(100)); + setDefaultValueForPref(prefDict, @"selected_account", @""); setDefaultValueForPref(prefDict, @"selected_version", @"1.7.10"); setDefaultValueForPref(prefDict, @"selected_version_type", @(0)); setDefaultValueForPref(envPrefDict, @"press_duration", @(400)); @@ -178,16 +179,3 @@ CGRect getDefaultSafeArea() { } return defaultSafeArea; } - -CFTypeRef SecTaskCopyValueForEntitlement(void* task, NSString* entitlement, CFErrorRef _Nullable *error); -void* SecTaskCreateFromSelf(CFAllocatorRef allocator); -BOOL getEntitlementValue(NSString *key) { - void *secTask = SecTaskCreateFromSelf(NULL); - CFTypeRef value = SecTaskCopyValueForEntitlement(SecTaskCreateFromSelf(NULL), key, nil); - if (value != nil) { - CFRelease(value); - } - CFRelease(secTask); - - return value != nil && [(__bridge id)value boolValue]; -} diff --git a/Natives/LauncherSplitViewController.m b/Natives/LauncherSplitViewController.m index 46cfc859ba..b0e24eb5a1 100644 --- a/Natives/LauncherSplitViewController.m +++ b/Natives/LauncherSplitViewController.m @@ -1,8 +1,11 @@ #import "LauncherSplitViewController.h" #import "LauncherMenuViewController.h" #import "LauncherNavigationController.h" +#import "LauncherPreferences.h" #import "utils.h" +extern NSMutableDictionary *prefDict; + @interface LauncherSplitViewController (){ } @end @@ -13,6 +16,7 @@ - (void)viewDidLoad { [super viewDidLoad]; UIApplication.sharedApplication.idleTimerDisabled = YES; setViewBackgroundColor(self.view); + setDefaultValueForPref(prefDict, @"control_safe_area", NSStringFromCGRect(getDefaultSafeArea())); self.delegate = self; [self changeDisplayModeForSize:self.view.frame.size]; @@ -22,6 +26,28 @@ - (void)viewDidLoad { detailVc.toolbarHidden = NO; self.viewControllers = @[[[UINavigationController alloc] initWithRootViewController:masterVc], detailVc]; + + if(roundf([[NSProcessInfo processInfo] physicalMemory] / 1048576) < 1900 && [getPreference(@"unsupported_warn_counter") intValue] == 0) { + UIAlertController *RAMAlert = [UIAlertController alertControllerWithTitle:localize(@"login.warn.title.a7", nil) message:localize(@"login.warn.message.a7", nil) preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *ok = [UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleCancel handler:nil]; + [self presentViewController:RAMAlert animated:YES completion:nil]; + [RAMAlert addAction:ok]; + } + + int launchNum = [getPreference(@"unsupported_warn_counter") intValue]; + if(launchNum > 0) { + setPreference(@"unsupported_warn_counter", @(launchNum - 1)); + } else { + setPreference(@"unsupported_warn_counter", @(30)); + } + + if(!getenv("POJAV_DETECTEDJB") && [getPreference(@"ram_unjb_warn") boolValue] == YES && [getPreference(@"auto_ram") boolValue] == NO) { + UIAlertController *ramalert = [UIAlertController alertControllerWithTitle:localize(@"login.warn.title.ram_unjb", nil) message:localize(@"login.warn.message.ram_unjb", nil) preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *ok = [UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleCancel handler:nil]; + [self presentViewController:ramalert animated:YES completion:nil]; + [ramalert addAction:ok]; + setPreference(@"ram_unjb_warn", @NO); + } } - (void)splitViewController:(UISplitViewController *)svc willChangeToDisplayMode:(UISplitViewControllerDisplayMode)displayMode { diff --git a/Natives/LoginViewController.h b/Natives/LoginViewController.h deleted file mode 100644 index 7dfae9ca53..0000000000 --- a/Natives/LoginViewController.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import "LauncherPreferences.h" - -@interface LoginViewController : UIViewController - -@end diff --git a/Natives/LoginViewController.m b/Natives/LoginViewController.m deleted file mode 100644 index 3717139bba..0000000000 --- a/Natives/LoginViewController.m +++ /dev/null @@ -1,434 +0,0 @@ -#include -#include - -#import "AFNetworking.h" -#import -#import "authenticator/BaseAuthenticator.h" -#import "ALTServerConnection.h" - -#import "AppDelegate.h" -#import "AccountListViewController.h" -#import "LauncherSplitViewController.h" -#import "LoginViewController.h" - -#import "LauncherPreferences.h" -#import "ios_uikit_bridge.h" -#import "utils.h" -#import "log.h" - -#define AUTORESIZE_BTN UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin -#define AUTORESIZE AUTORESIZE_BTN | UIViewAutoresizingFlexibleBottomMargin - -#define TYPE_SELECTACC 0 -#define TYPE_MICROSOFT 1 -#define TYPE_OFFLINE 2 - -extern NSMutableDictionary *prefDict; - -#pragma mark - LoginViewController -@interface LoginViewController () {} -@property(nonatomic) ASWebAuthenticationSession *authVC; -@end - -@implementation LoginViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - setViewBackgroundColor(self.view); - - setDefaultValueForPref(prefDict, @"control_safe_area", NSStringFromCGRect(getDefaultSafeArea())); - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(msaLoginCallback:) name:@"MSALoginCallback" object:nil]; - - CGFloat width = self.view.frame.size.width; - CGFloat height = self.view.frame.size.height; // - self.navigationController.navigationBar.frame.size.height; - CGFloat rawHeight = self.view.frame.size.height; - - if(roundf([[NSProcessInfo processInfo] physicalMemory] / 1048576) < 1900 && [getPreference(@"unsupported_warn_counter") intValue] == 0) { - UIAlertController *RAMAlert = [UIAlertController alertControllerWithTitle:localize(@"login.warn.title.a7", nil) message:localize(@"login.warn.message.a7", nil) preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction *ok = [UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleCancel handler:nil]; - [self presentViewController:RAMAlert animated:YES completion:nil]; - [RAMAlert addAction:ok]; - } - - int launchNum = [getPreference(@"unsupported_warn_counter") intValue]; - if(launchNum > 0) { - setPreference(@"unsupported_warn_counter", @(launchNum - 1)); - } else { - setPreference(@"unsupported_warn_counter", @(30)); - } - - if(!getenv("POJAV_DETECTEDJB") && [getPreference(@"ram_unjb_warn") boolValue] == YES && [getPreference(@"auto_ram") boolValue] == NO) { - UIAlertController *ramalert = [UIAlertController alertControllerWithTitle:localize(@"login.warn.title.ram_unjb", nil) message:localize(@"login.warn.message.ram_unjb", nil) preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction *ok = [UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleCancel handler:nil]; - [self presentViewController:ramalert animated:YES completion:nil]; - [ramalert addAction:ok]; - setPreference(@"ram_unjb_warn", @NO); - } - - CGFloat widthSplit = width / 4.0; - CGFloat widthSplit2 = width / 2.0; - - for (int i = 0; i < 5; i++) { - NSLog(@"%@", [UIImage imageNamed:@"AppLogo-Vector"]); - } - UIImageView *logoView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"AppLogo-Vector"]]; - logoView.frame = CGRectMake(0, (rawHeight / 2) - 125, width, 250); - [logoView setContentMode:UIViewContentModeScaleAspectFit]; - logoView.autoresizingMask = AUTORESIZE; - [self.view addSubview:logoView]; - - UIImageView *workMarkView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"AppLogo"]]; - workMarkView.frame = CGRectMake(0, (rawHeight / 2) - 35, width, 70); - [workMarkView setContentMode:UIViewContentModeScaleAspectFit]; - workMarkView.autoresizingMask = AUTORESIZE; - [self.view addSubview:workMarkView]; - - NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init]; - [dateFormatter setDateFormat:@"MM-dd"]; - NSString* date = [dateFormatter stringFromDate:[NSDate date]]; - - CGRect frame = CGRectMake(widthSplit2 - (((width - widthSplit * 2.0) / 2) / 2), (height - 80.0), (width - widthSplit * 2.0) / 2, 40.0); - - UIButton *button_login = [UIButton buttonWithType:UIButtonTypeSystem]; - setButtonPointerInteraction(button_login); - [button_login setTitle:localize(@"Sign in", nil) forState:UIControlStateNormal]; - button_login.autoresizingMask = AUTORESIZE_BTN; - button_login.frame = CGRectMake(frame.origin.x - frame.size.width - 20, (height - 80.0), (width - widthSplit * 2.0) / 2, 40.0); - if([date isEqualToString:@"06-29"] || [date isEqualToString:@"06-30"] || [date isEqualToString:@"07-01"]) { - button_login.backgroundColor = [UIColor colorWithRed:67/255.0 green:0/255.0 blue:8/255.0 alpha:1.0]; - } else { - button_login.backgroundColor = [UIColor colorWithRed:54/255.0 green:176/255.0 blue:48/255.0 alpha:1.0]; - } - button_login.layer.cornerRadius = 5; - [button_login setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; - [button_login addTarget:self action:@selector(accountType:) forControlEvents:UIControlEventTouchUpInside]; - if(@available (iOS 14.0, *)) { - UIAction *option1 = [UIAction actionWithTitle:localize(@"login.option.microsoft", nil) image:nil identifier:nil - handler:^(__kindof UIAction * _Nonnull action) {[self loginMicrosoft];}]; - UIAction *option3 = [UIAction actionWithTitle:localize(@"login.option.demo", nil) image:nil identifier:nil - handler:^(__kindof UIAction * _Nonnull action) {[self loginDemo:button_login];}]; - UIAction *option4 = [UIAction actionWithTitle:localize(@"login.option.local", nil) image:nil identifier:nil - handler:^(__kindof UIAction * _Nonnull action) {[self loginOffline:button_login];}]; - UIMenu *menu = [UIMenu menuWithTitle:@"" image:nil identifier:nil - options:UIMenuOptionsDisplayInline children:@[option4, option3, option1]]; - button_login.menu = menu; - button_login.showsMenuAsPrimaryAction = YES; - } - if(@available (iOS 13.0, *)) { - button_login.imageView.image = [[UIImage systemImageNamed:@"person.crop.circle.badge.plus"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - [button_login setTintColor:UIColor.whiteColor]; - [button_login setImage:button_login.imageView.image forState:UIControlStateNormal]; - } - [self.view addSubview:button_login]; - - UIButton *button_accounts = [UIButton buttonWithType:UIButtonTypeSystem]; - setButtonPointerInteraction(button_accounts); - [button_accounts setTitle:localize(@"Accounts", nil) forState:UIControlStateNormal]; - button_accounts.autoresizingMask = AUTORESIZE_BTN; - button_accounts.frame = CGRectMake(frame.origin.x + frame.size.width + 20, (height - 80.0), (width - widthSplit * 2.0) / 2, 40.0); - if([date isEqualToString:@"06-29"] || [date isEqualToString:@"06-30"] || [date isEqualToString:@"07-01"]) { - button_accounts.backgroundColor = [UIColor colorWithRed:67/255.0 green:0/255.0 blue:8/255.0 alpha:1.0]; - } else { - button_accounts.backgroundColor = [UIColor colorWithRed:54/255.0 green:176/255.0 blue:48/255.0 alpha:1.0]; - } - button_accounts.layer.cornerRadius = 5; - [button_accounts setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; - [button_accounts addTarget:self action:@selector(loginAccount:) forControlEvents:UIControlEventTouchUpInside]; - if(@available (iOS 13.0, *)) { - button_accounts.imageView.image = [[UIImage systemImageNamed:@"person.crop.circle"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - [button_accounts setTintColor:UIColor.whiteColor]; - [button_accounts setImage:button_accounts.imageView.image forState:UIControlStateNormal]; - } - [self.view addSubview:button_accounts]; - - if([date isEqualToString:@"06-29"] || [date isEqualToString:@"06-30"] || [date isEqualToString:@"07-01"]) { - UILabel *technoNote = [[UILabel alloc] initWithFrame:CGRectMake(0, height - 60, width, 40.0)]; - technoNote.text = @"Technoblade never dies!"; - technoNote.lineBreakMode = NSLineBreakByWordWrapping; - technoNote.numberOfLines = 1; - [technoNote setFont:[UIFont boldSystemFontOfSize:10]]; - technoNote.textAlignment = NSTextAlignmentCenter; - [self.view addSubview:technoNote]; - } - - if (!getEntitlementValue(@"dynamic-codesigning")) { - if (isJITEnabled()) { - self.title = localize(@"login.jit.enabled", nil); - } else { - [self enableJITWithJitStreamer]; - } - } -} - -- (void)displayProgress:(NSString *)title { - self.title = title; - UIActivityIndicatorViewStyle style; - if (@available(iOS 13.0, *)) { - style = UIActivityIndicatorViewStyleMedium; - } else { - style = UIActivityIndicatorViewStyleGray; - } - UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:style]; - self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:indicator]; - [indicator startAnimating]; -} - -- (void)accountType:(UIButton *)sender { - if(@available (iOS 14.0, *)) { - // UIMenu - } else { - UIAlertController *fullAlert = [UIAlertController alertControllerWithTitle:@"Let's get you signed in." message:@"What account do you use to log into Minecraft?" preferredStyle:UIAlertControllerStyleActionSheet]; - UIAlertAction *microsoft = [UIAlertAction actionWithTitle:localize(@"login.option.microsoft", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self loginMicrosoft];}]; - UIAlertAction *offline = [UIAlertAction actionWithTitle:localize(@"login.option.local", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self loginOffline:sender];}]; - UIAlertAction *demo = [UIAlertAction actionWithTitle:localize(@"login.option.demo", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self loginDemo:sender];}]; - UIAlertAction *cancel = [UIAlertAction actionWithTitle:localize(localize(@"Cancel", nil), nil) style:UIAlertActionStyleCancel handler:nil]; - [self setPopoverProperties:fullAlert.popoverPresentationController sender:sender]; - [self presentViewController:fullAlert animated:YES completion:nil]; - [fullAlert addAction:microsoft]; - [fullAlert addAction:demo]; - [fullAlert addAction:offline]; - [fullAlert addAction:cancel]; - } -} - -- (void)loginAccountInput:(int)type data:(NSString *)data { - __block BOOL shouldDismiss = NO; - UIAlertController *alert; - BaseAuthenticator *auth; - - switch (type) { - case TYPE_MICROSOFT: - auth = [[MicrosoftAuthenticator alloc] initWithInput:data]; - break; - case TYPE_OFFLINE: - auth = [[LocalAuthenticator alloc] initWithInput:data]; - break; - case TYPE_SELECTACC: { - NSString *fileContent = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%s/accounts/%@.json", getenv("POJAV_HOME"), data] encoding:NSUTF8StringEncoding error:nil]; - shouldDismiss = [fileContent rangeOfString:@"\"accessToken\": \"0\","].location != NSNotFound; - auth = [BaseAuthenticator loadSavedName:data]; - } break; - } - - if (auth == nil) return; - - if (type != TYPE_OFFLINE && !shouldDismiss) { - [self displayProgress:localize(@"login.progress.title", nil)]; - } - - id callback = ^(BOOL success) { - self.title = @""; - self.navigationItem.leftBarButtonItem = nil; - if (!success) return; - - LauncherSplitViewController *splitVc; - if (@available(iOS 14.0, tvOS 14.0, *)) { - splitVc = [[LauncherSplitViewController alloc] initWithStyle:UISplitViewControllerStyleDoubleColumn]; - } else { - splitVc = [[LauncherSplitViewController alloc] init]; - } - splitVc.modalPresentationStyle = UIModalPresentationFullScreen; - [self presentViewController:splitVc animated:YES completion:nil]; - }; - if (type == TYPE_SELECTACC) { - [auth refreshTokenWithCallback:callback]; - } else { - [auth loginWithCallback:callback]; - } -} - -- (void)loginMicrosoft { - NSURL *url = [NSURL URLWithString:@"https://login.live.com/oauth20_authorize.srf?client_id=00000000402b5328&response_type=code&scope=service%3A%3Auser.auth.xboxlive.com%3A%3AMBI_SSL&redirect_url=https%3A%2F%2Flogin.live.com%2Foauth20_desktop.srf"]; - - self.authVC = - [[ASWebAuthenticationSession alloc] initWithURL:url - callbackURLScheme:@"ms-xal-00000000402b5328" - completionHandler:^(NSURL * _Nullable callbackURL, - NSError * _Nullable error) { - if (callbackURL != nil) { - NSString *urlString = [callbackURL absoluteString]; - // NSLog(@"URL returned = %@", [callbackURL absoluteString]); - - if ([urlString containsString:@"/auth/?code="] == YES) { - NSArray *components = [urlString componentsSeparatedByString:@"/auth/?code="]; - [self loginAccountInput:TYPE_MICROSOFT data:components[1]]; - } else { - NSArray *components = [urlString componentsSeparatedByString:@"/auth/?error="]; - if ([components[1] hasPrefix:@"access_denied"] == NO) { - NSString *outError = [components[1] - stringByReplacingOccurrencesOfString:@"&error_description=" withString:@": "]; - outError = [outError stringByRemovingPercentEncoding]; - showDialog(self, localize(@"Error", nil), outError); - } - } - } else { - if (error.code != ASWebAuthenticationSessionErrorCodeCanceledLogin) { - showDialog(self, localize(@"Error", nil), error.localizedDescription); - } - } - }]; - - if (@available(iOS 13.0, *)) { - self.authVC.prefersEphemeralWebBrowserSession = YES; - self.authVC.presentationContextProvider = self; - } - - if ([self.authVC start] == NO) { - showDialog(self, localize(@"Error", nil), @"Unable to open Safari"); - } -} - -- (void)loginOffline:(UIButton *)sender { - if ([getPreference(@"local_warn") boolValue] == YES) { - UIAlertController *offlineAlert = [UIAlertController alertControllerWithTitle:localize(@"login.warn.title.localmode", nil) message:localize(@"login.warn.message.localmode", nil) preferredStyle:UIAlertControllerStyleActionSheet]; - [self setPopoverProperties:offlineAlert.popoverPresentationController sender:sender]; - UIAlertAction *ok = [UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self loginOffline:sender];}]; - UIAlertAction *cancel = [UIAlertAction actionWithTitle:localize(@"Cancel", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self accountType:sender];}]; - [self presentViewController:offlineAlert animated:YES completion:nil]; - [offlineAlert addAction:ok]; - [offlineAlert addAction:cancel]; - setPreference(@"local_warn", @NO); - } else { - UIAlertController *controller = [UIAlertController alertControllerWithTitle:localize(@"Sign in", nil) message:localize(@"login.option.local", nil) preferredStyle:UIAlertControllerStyleAlert]; - [controller addTextFieldWithConfigurationHandler:^(UITextField *textField) { - textField.placeholder = localize(@"login.alert.field.username", nil); - textField.clearButtonMode = UITextFieldViewModeWhileEditing; - textField.borderStyle = UITextBorderStyleRoundedRect; - }]; - [controller addAction:[UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { - NSArray *textFields = controller.textFields; - UITextField *usernameField = textFields[0]; - if (usernameField.text.length < 3 || usernameField.text.length > 16) { - controller.message = localize(@"login.error.username.outOfRange", nil); - [self presentViewController:controller animated:YES completion:nil]; - } else { - [self loginAccountInput:TYPE_OFFLINE data:usernameField.text]; - } - }]]; - [controller addAction:[UIAlertAction actionWithTitle:localize(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil]]; - [self presentViewController:controller animated:YES completion:nil]; - } -} - -- (void)loginDemo:(UIButton *)sender { - if ([getPreference(@"demo_warn") boolValue] == YES) { - UIAlertController *offlineAlert = [UIAlertController alertControllerWithTitle:localize(@"login.warn.title.demomode", nil) message:localize(@"login.warn.message.demomode", nil) preferredStyle:UIAlertControllerStyleActionSheet]; - [self setPopoverProperties:offlineAlert.popoverPresentationController sender:sender]; - UIAlertAction *ok = [UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self loginMicrosoft];}]; - UIAlertAction *cancel = [UIAlertAction actionWithTitle:localize(@"Cancel", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self accountType:sender];}]; - [self presentViewController:offlineAlert animated:YES completion:nil]; - [offlineAlert addAction:ok]; - [offlineAlert addAction:cancel]; - setPreference(@"demo_warn", @NO); - } else { - [self loginMicrosoft]; - } -} - -- (void)loginAccount:(UIButton *)sender { - AccountListViewController *vc = [[AccountListViewController alloc] init]; -/* - vc.whenDelete = ^void(NSString* name) { - UIAlertController* alert = showLoadingDialog(vc, @"Logging out..."); - }; -*/ - vc.whenItemSelected = ^void(NSString* name) { - [self loginAccountInput:TYPE_SELECTACC data:name]; - }; - vc.modalPresentationStyle = UIModalPresentationPopover; - vc.preferredContentSize = CGSizeMake(350, 250); - - UIPopoverPresentationController *popoverController = [vc popoverPresentationController]; - [self setPopoverProperties:popoverController sender:sender]; - popoverController.permittedArrowDirections = UIPopoverArrowDirectionAny; - popoverController.delegate = vc; - [self presentViewController:vc animated:YES completion:nil]; -} - -- (void)enableJITWithAltJIT -{ - self.title = localize(@"login.jit.start.AltKit", nil); - [ALTServerManager.sharedManager startDiscovering]; - [ALTServerManager.sharedManager autoconnectWithCompletionHandler:^(ALTServerConnection *connection, NSError *error) { - if (error) { - NSLog(@"[AltKit] Could not auto-connect to server. %@", error); - self.title = localize(@"login.jit.fail.AltKit", nil); - return; - } - [connection enableUnsignedCodeExecutionWithCompletionHandler:^(BOOL success, NSError *error) { - if (success) { - NSLog(@"[AltKit] Successfully enabled JIT compilation!"); - [ALTServerManager.sharedManager stopDiscovering]; - self.title = localize(@"login.jit.enabled", nil); - self.navigationItem.leftBarButtonItem = nil; - } else { - NSLog(@"[AltKit] Could not enable JIT compilation. %@", error); - self.title = localize(@"login.jit.fail.AltKit", nil); - self.navigationItem.leftBarButtonItem = nil; - showDialog(self, localize(@"Error", nil), error.description); - } - [connection disconnect]; - }]; - }]; -} - -- (void)enableJITWithJitStreamer -{ - [self displayProgress:localize(@"login.jit.checking", nil)]; - - // TODO: customizable address - NSString *address = getPreference(@"jitstreamer_server"); - debugLog("JitStreamer server is %s, attempting to connect...", address.UTF8String); - - AFHTTPSessionManager *manager = AFHTTPSessionManager.manager; - manager.requestSerializer.timeoutInterval = 10; - manager.responseSerializer = AFHTTPResponseSerializer.serializer; - manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/plain", nil]; - [manager GET:[NSString stringWithFormat:@"http://%@/version", address] parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask *task, NSData *response) { - NSString *version = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding]; - NSLog(@"Found JitStreamer %@", version); - self.title = localize(@"login.jit.found.JitStreamer", nil); - manager.requestSerializer.timeoutInterval = 0; - manager.responseSerializer = AFJSONResponseSerializer.serializer; - void(^handleResponse)(NSURLSessionDataTask *task, id response) = ^void(NSURLSessionDataTask *task, id response){ - self.navigationItem.leftBarButtonItem = nil; - NSDictionary *responseDict; - // FIXME: successful response may fail due to serialization issues - if ([response isKindOfClass:NSError.class]) { - NSLog(@"Error?: %@", responseDict); - NSData *errorData = ((NSError *)response).userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]; - responseDict = [NSJSONSerialization JSONObjectWithData:errorData options:0 error:nil]; - [self enableJITWithAltJIT]; - } else { - responseDict = response; - } - if ([responseDict[@"success"] boolValue]) { - self.title = localize(@"login.jit.enabled", nil); - self.navigationItem.leftBarButtonItem = nil; - } else { - self.title = [NSString stringWithFormat:localize(@"login.jit.fail.JitStreamer", nil), responseDict[@"message"]]; - self.navigationItem.leftBarButtonItem = nil; - showDialog(self, localize(@"Error", nil), responseDict[@"message"]); - [self enableJITWithAltJIT]; - } - }; - [manager POST:[NSString stringWithFormat:@"http://%@/attach/%d/", address, getpid()] parameters:nil headers:nil progress:nil success:handleResponse failure:handleResponse]; - } failure:^(NSURLSessionDataTask *task, NSError *error) { - [self enableJITWithAltJIT]; - }]; -} - -- (void)setPopoverProperties:(UIPopoverPresentationController *)controller sender:(UIButton *)sender { - if (controller != nil) { - controller.sourceView = sender; - controller.sourceRect = sender.bounds; - } -} - -#pragma mark - ASWebAuthenticationPresentationContextProviding -- (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session API_AVAILABLE(ios(13.0)){ - return UIApplication.sharedApplication.windows.firstObject; -} - -@end diff --git a/Natives/MinecraftResourceUtils.m b/Natives/MinecraftResourceUtils.m index 5671391a68..f25c396199 100644 --- a/Natives/MinecraftResourceUtils.m +++ b/Natives/MinecraftResourceUtils.m @@ -417,6 +417,7 @@ + (BOOL)downloadClientAssets:(NSDictionary *)assets progress:(NSProgress *)mainP NSError *err; [NSFileManager.defaultManager createDirectoryAtPath:path.stringByDeletingLastPathComponent withIntermediateDirectories:YES attributes:nil error:&err]; //NSLog(@"path %@ err %@", path, err); + usleep(50000); // avoid overloading queue callback([NSString stringWithFormat:@"Downloading %@", name], nil); NSString *url = [NSString stringWithFormat:@"https://resources.download.minecraft.net/%@", pathname]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]]; diff --git a/Natives/SceneDelegate.m b/Natives/SceneDelegate.m index 1b0bf02611..e9b3e3c8ed 100644 --- a/Natives/SceneDelegate.m +++ b/Natives/SceneDelegate.m @@ -1,5 +1,4 @@ #import "SceneDelegate.h" -#import "LoginViewController.h" #import "ios_uikit_bridge.h" #import "utils.h" @@ -59,11 +58,4 @@ - (void)sceneDidEnterBackground:(UIScene *)scene API_AVAILABLE(ios(13.0)){ CallbackBridge_setWindowAttrib(GLFW_VISIBLE, 0); } -- (void)scene:(UIScene *)scene openURLContexts:(nonnull NSSet *)URLContexts -API_AVAILABLE(ios(13.0)){ - NSURL *url = [[URLContexts allObjects] firstObject].URL; - NSDictionary *data = [NSDictionary dictionaryWithObject:url forKey:@"url"]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"MSALoginCallback" object:self userInfo:data]; -} - @end diff --git a/Natives/UIKit+hook.m b/Natives/UIKit+hook.m index 3d53517023..bcecb092e9 100644 --- a/Natives/UIKit+hook.m +++ b/Natives/UIKit+hook.m @@ -26,17 +26,6 @@ - (UIUserInterfaceIdiom)hook_userInterfaceIdiom { @end -@implementation UIToolbar(hook) - -- (CGSize)hook_sizeThatFits:(CGSize)size { - // Make the toolbar taller - CGSize ret = [self hook_sizeThatFits:size]; - ret.height += 50; - return ret; -} - -@end - @implementation UITraitCollection(hook) - (UIUserInterfaceSizeClass)horizontalSizeClass { diff --git a/Natives/authenticator/BaseAuthenticator.h b/Natives/authenticator/BaseAuthenticator.h index 5afb77be80..3550cd1878 100644 --- a/Natives/authenticator/BaseAuthenticator.h +++ b/Natives/authenticator/BaseAuthenticator.h @@ -1,6 +1,6 @@ #import "Foundation/Foundation.h" -typedef void(^Callback)(BOOL); +typedef void(^Callback)(NSString* status, BOOL success); @interface BaseAuthenticator : NSObject diff --git a/Natives/authenticator/BaseAuthenticator.m b/Natives/authenticator/BaseAuthenticator.m index 7eb00ba2a2..53bb5b3433 100644 --- a/Natives/authenticator/BaseAuthenticator.m +++ b/Natives/authenticator/BaseAuthenticator.m @@ -9,7 +9,7 @@ @implementation BaseAuthenticator + (id)current { if (current == nil) { - [self loadSavedName:getPreference(@"internal_selected_account")]; + [self loadSavedName:getPreference(@"selected_account")]; } return current; } @@ -21,7 +21,10 @@ + (void)setCurrent:(BaseAuthenticator *)auth { + (id)loadSavedName:(NSString *)name { NSMutableDictionary *authData = parseJSONFromFile([NSString stringWithFormat:@"%s/accounts/%@.json", getenv("POJAV_HOME"), name]); if (authData[@"error"] != nil) { - showDialog(currentVC(), localize(@"Error", nil), ((NSError *)authData[@"error"]).localizedDescription); + NSError *error = ((NSError *)authData[@"error"]); + if (error.code != NSFileReadNoSuchFileError) { + showDialog(currentVC(), localize(@"Error", nil), error.localizedDescription); + } return nil; } if ([authData[@"accessToken"] length] < 5) { @@ -43,10 +46,10 @@ - (id)initWithInput:(NSString *)string { return [self initWithData:data]; } -- (void)loginWithCallback:(void (^)(BOOL success))callback { +- (void)loginWithCallback:(Callback)callback { } -- (void)refreshTokenWithCallback:(void (^)(BOOL success))callback { +- (void)refreshTokenWithCallback:(Callback)callback { } - (BOOL)saveChanges { diff --git a/Natives/authenticator/LocalAuthenticator.m b/Natives/authenticator/LocalAuthenticator.m index 0e15d521ac..3d6a0a5c21 100644 --- a/Natives/authenticator/LocalAuthenticator.m +++ b/Natives/authenticator/LocalAuthenticator.m @@ -6,12 +6,12 @@ - (void)loginWithCallback:(Callback)callback { self.authData[@"oldusername"] = self.authData[@"username"] = self.authData[@"input"]; self.authData[@"accessToken"] = @"0"; self.authData[@"profileId"] = @"00000000-0000-0000-0000-000000000000"; - callback([super saveChanges]); + callback(nil, [super saveChanges]); } - (void)refreshTokenWithCallback:(Callback)callback { // Nothing to do - callback(YES); + callback(nil, YES); } @end diff --git a/Natives/authenticator/MicrosoftAuthenticator.m b/Natives/authenticator/MicrosoftAuthenticator.m index 0fd95ea20e..2997e6258e 100644 --- a/Natives/authenticator/MicrosoftAuthenticator.m +++ b/Natives/authenticator/MicrosoftAuthenticator.m @@ -8,7 +8,7 @@ @implementation MicrosoftAuthenticator - (void)acquireAccessToken:(NSString *)authcode refresh:(BOOL)refresh callback:(Callback)callback { - currentVC().title = localize(@"login.msa.progress.acquireAccessToken", nil); + callback(localize(@"login.msa.progress.acquireAccessToken", nil), YES); NSDictionary *data = @{ @"client_id": @"00000000402b5328", @@ -23,14 +23,18 @@ - (void)acquireAccessToken:(NSString *)authcode refresh:(BOOL)refresh callback:( self.authData[@"msaRefreshToken"] = response[@"refresh_token"]; [self acquireXBLToken:response[@"access_token"] callback:callback]; } failure:^(NSURLSessionDataTask *task, NSError *error) { - callback(NO); - NSLog(@"[MSA] Error: %@", error); - showDialog(currentVC(), @"Error", error.localizedDescription); + if (error.code == NSURLErrorDataNotAllowed) { + // The account token is expired and offline + self.authData[@"accessToken"] = @"offline"; + callback(nil, YES); + } else { + callback(error.localizedDescription, NO); + } }]; } - (void)acquireXBLToken:(NSString *)accessToken callback:(Callback)callback { - currentVC().title = localize(@"login.msa.progress.acquireXBLToken", nil); + callback(localize(@"login.msa.progress.acquireXBLToken", nil), YES); NSDictionary *data = @{ @"Properties": @{ @@ -45,39 +49,37 @@ - (void)acquireXBLToken:(NSString *)accessToken callback:(Callback)callback { AFHTTPSessionManager *manager = AFHTTPSessionManager.manager; manager.requestSerializer = AFJSONRequestSerializer.serializer; [manager POST:@"https://user.auth.xboxlive.com/user/authenticate" parameters:data headers:nil progress:nil success:^(NSURLSessionDataTask *task, NSDictionary *response) { - Callback innerCallback = ^(BOOL success) { + Callback innerCallback = ^(NSString* status, BOOL success) { if (success == NO) { - callback(NO); + callback(status, NO); return; } // Obtain XSTS for authenticating to Minecraft - [self acquireXSTSFor:@"rp://api.minecraftservices.com/" token:response[@"Token"] callback:^(NSString *xsts, NSString *uhs){ + [self acquireXSTSFor:@"rp://api.minecraftservices.com/" token:response[@"Token"] xstsCallback:^(NSString *xsts, NSString *uhs){ if (xsts == nil) { - callback(NO); + callback(nil, NO); return; } self.authData[@"xuid"] = uhs; [self acquireMinecraftToken:uhs xstsToken:xsts callback:callback]; - }]; + } callback:callback]; }; // Obtain XSTS for getting the Xbox gamertag - [self acquireXSTSFor:@"http://xboxlive.com" token:response[@"Token"] callback:^(NSString *xsts, NSString *uhs){ + [self acquireXSTSFor:@"http://xboxlive.com" token:response[@"Token"] xstsCallback:^(NSString *xsts, NSString *uhs){ if (xsts == nil) { - callback(NO); + callback(nil, NO); return; } [self acquireXboxProfile:uhs xstsToken:xsts callback:innerCallback]; - }]; + } callback:callback]; } failure:^(NSURLSessionDataTask *task, NSError *error) { - callback(NO); - NSLog(@"[MSA] Error: %@", error); - showDialog(currentVC(), @"Error", error.localizedDescription); + callback(error.localizedDescription, NO); }]; } -- (void)acquireXSTSFor:(NSString *)replyingParty token:(NSString *)xblToken callback:(XSTSCallback)callback { - currentVC().title = localize(@"login.msa.progress.acquireXSTS", nil); +- (void)acquireXSTSFor:(NSString *)replyingParty token:(NSString *)xblToken xstsCallback:(XSTSCallback)xstsCallback callback:(Callback)callback { + callback(localize(@"login.msa.progress.acquireXSTS", nil), YES); NSDictionary *data = @{ @"Properties": @{ @@ -94,12 +96,15 @@ - (void)acquireXSTSFor:(NSString *)replyingParty token:(NSString *)xblToken call manager.requestSerializer = AFJSONRequestSerializer.serializer; [manager POST:@"https://xsts.auth.xboxlive.com/xsts/authorize" parameters:data headers:nil progress:nil success:^(NSURLSessionDataTask *task, NSDictionary *response) { NSString *uhs = response[@"DisplayClaims"][@"xui"][0][@"uhs"]; - callback(response[@"Token"], uhs); + xstsCallback(response[@"Token"], uhs); } failure:^(NSURLSessionDataTask *task, NSError *error) { - callback(nil, nil); NSString *errorString; NSData *errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]; - NSDictionary *errorDict = [NSJSONSerialization JSONObjectWithData: errorData options:kNilOptions error:nil]; + if (errorData == nil) { + callback(error.localizedDescription, NO); + return; + } + NSDictionary *errorDict = [NSJSONSerialization JSONObjectWithData:errorData options:kNilOptions error:nil]; switch ((int)([errorDict[@"XErr"] longValue]-2148916230l)) { case 3: errorString = @"login.msa.error.xsts.noxboxacc"; @@ -118,14 +123,13 @@ - (void)acquireXSTSFor:(NSString *)replyingParty token:(NSString *)xblToken call errorString = [NSString stringWithFormat:@"%@\n\nUnknown XErr code, response:\n%@", error.localizedDescription, errorDict]; break; } - NSLog(@"[MSA] Error: %@", errorString); - showDialog(currentVC(), localize(@"Error", nil), localize(errorString, nil)); + callback(localize(errorString, nil), NO); }]; } - (void)acquireXboxProfile:(NSString *)xblUhs xstsToken:(NSString *)xblXsts callback:(Callback)callback { - currentVC().title = localize(@"login.msa.progress.acquireXboxProfile", nil); + callback(localize(@"login.msa.progress.acquireXboxProfile", nil), YES); NSDictionary *headers = @{ @"x-xbl-contract-version": @"2", @@ -136,16 +140,14 @@ - (void)acquireXboxProfile:(NSString *)xblUhs xstsToken:(NSString *)xblXsts call [manager GET:@"https://profile.xboxlive.com/users/me/profile/settings?settings=PublicGamerpic,Gamertag" parameters:nil headers:headers progress:nil success:^(NSURLSessionDataTask *task, NSDictionary *response) { self.authData[@"profilePicURL"] = [NSString stringWithFormat:@"%@&h=120&w=120", response[@"profileUsers"][0][@"settings"][0][@"value"]]; self.authData[@"xboxGamertag"] = response[@"profileUsers"][0][@"settings"][1][@"value"]; - callback(YES); + callback(nil, YES); } failure:^(NSURLSessionDataTask *task, NSError *error) { - callback(NO); - NSLog(@"[MSA] Error: %@", error); - showDialog(currentVC(), @"Error", error.localizedDescription); + callback(error.localizedDescription, NO); }]; } - (void)acquireMinecraftToken:(NSString *)xblUhs xstsToken:(NSString *)xblXsts callback:(Callback)callback { - currentVC().title = localize(@"login.msa.progress.acquireMCToken", nil); + callback(localize(@"login.msa.progress.acquireMCToken", nil), YES); NSDictionary *data = @{ @"identityToken": [NSString stringWithFormat:@"XBL3.0 x=%@;%@", xblUhs, xblXsts] @@ -157,16 +159,14 @@ - (void)acquireMinecraftToken:(NSString *)xblUhs xstsToken:(NSString *)xblXsts c self.authData[@"accessToken"] = response[@"access_token"]; [self checkMCProfile:response[@"access_token"] callback:callback]; } failure:^(NSURLSessionDataTask *task, NSError *error) { - callback(NO); - NSLog(@"[MSA] Error: %@", error); - showDialog(currentVC(), @"Error", error.localizedDescription); + callback(error.localizedDescription, NO); }]; } - (void)checkMCProfile:(NSString *)mcAccessToken callback:(Callback)callback { self.authData[@"expiresAt"] = @((long)[NSDate.date timeIntervalSince1970] + 86400); - currentVC().title = localize(@"login.msa.progress.checkMCProfile", nil); + callback(localize(@"login.msa.progress.checkMCProfile", nil), YES); NSDictionary *headers = @{ @"Authorization": [NSString stringWithFormat:@"Bearer %@", mcAccessToken] @@ -185,7 +185,7 @@ - (void)checkMCProfile:(NSString *)mcAccessToken callback:(Callback)callback { self.authData[@"profilePicURL"] = [NSString stringWithFormat:@"https://mc-heads.net/head/%@/120", self.authData[@"profileId"]]; self.authData[@"oldusername"] = self.authData[@"username"]; self.authData[@"username"] = response[@"name"]; - callback([super saveChanges]); + callback(nil, [super saveChanges]); } failure:^(NSURLSessionDataTask *task, NSError *error) { NSData *errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]; NSDictionary *errorDict = [NSJSONSerialization JSONObjectWithData: errorData options:kNilOptions error:nil]; @@ -194,14 +194,12 @@ - (void)checkMCProfile:(NSString *)mcAccessToken callback:(Callback)callback { self.authData[@"profileId"] = @"00000000-0000-0000-0000-000000000000"; self.authData[@"username"] = [NSString stringWithFormat:@"Demo.%@", self.authData[@"xboxGamertag"]]; - showDialog(currentVC(), localize(@"Notice", nil), localize(@"login.msa.notice.demomode", nil)); - callback([super saveChanges]); + showDialog(currentVC(), localize(@"login.warn.title.demomode", nil), localize(@"login.warn.message.demomode", nil)); + callback(nil, [super saveChanges]); return; } - callback(NO); - NSLog(@"[MSA] Error: %@", errorDict); - showDialog(currentVC(), @"Error", [[NSString alloc] initWithData:errorData encoding:NSUTF8StringEncoding]); + callback([[NSString alloc] initWithData:errorData encoding:NSUTF8StringEncoding], NO); }]; } @@ -213,7 +211,7 @@ - (void)refreshTokenWithCallback:(Callback)callback { if ([NSDate.date timeIntervalSince1970] > [self.authData[@"expiresAt"] longValue]) { [self acquireAccessToken:self.authData[@"msaRefreshToken"] refresh:YES callback:callback]; } else { - callback(YES); + callback(nil, YES); } } diff --git a/Natives/en.lproj/Localizable.strings b/Natives/en.lproj/Localizable.strings index 5541e87dea..b52260c6aa 100644 --- a/Natives/en.lproj/Localizable.strings +++ b/Natives/en.lproj/Localizable.strings @@ -80,18 +80,13 @@ "login.jit.fail.AltKit" = "AltKit error: %@"; // Login buttons -"Sign in" = "Sign in"; -"FAQ" = "FAQ"; -"Accounts" = "Accounts"; "login.menu.sendlogs" = "Send your logs"; -"login.menu.updates" = "Recent updates"; +"login.option.add" = "Add account"; +"login.option.select" = "Select account"; "login.option.microsoft" = "Microsoft account"; -"login.option.demo" = "Demo account"; "login.option.local" = "Local account"; -"login.progress.title" = "Logging in"; - "login.warn.title.ram_unjb" = "Memory slider is enabled"; "login.warn.message.ram_unjb" = "Be aware that changing this setting can cause game instability or break the ability to play at all."; @@ -104,7 +99,7 @@ "login.warn.title.2gb" = "Your device has limited RAM."; "login.warn.message.2gb" = "Devices with less than 3GB of memory may run into memory constraints. We recommend leaving the automatic selector on for your device."; -"login.warn.title.demomode" = "This option is in beta."; +"login.warn.title.demomode" = "Your account is Demo"; "login.warn.message.demomode" = "As a replacement to offline and local mode, demo mode will allow you to sign into a Microsoft account that does not own the game and play the Java Edition trial, introduced in 1.3.1 and newer versions. Older versions contain the full game, as the official launcher does not place these restrictions. See our website to learn more about this transition from offline mode."; "login.warn.title.localmode" = "Offline mode is now Local mode."; @@ -115,8 +110,6 @@ "login.msa.error.xsts.krverify" = "Your account needs to be verified by your parents before you can continue.\nError code: 2148916236 or 2148916237."; "login.msa.error.xsts.underage" = "Your account needs to be linked to a parent account before you can continue.\nError code: 2148916238"; -"login.msa.notice.demomode" = "Could not find a Minecraft profile on this account. The account is now set to Demo mode. Did you buy Minecraft, or is there an active Xbox Game Pass subscription? Please configure your profile at https://minecraft.net/msaprofile to restore access to Online mode."; - "login.msa.progress.acquireAccessToken" = "(1/5) Acquiring the Access token"; "login.msa.progress.acquireXBLToken" = "(2/5) Acquiring the Xbox Live token"; "login.msa.progress.acquireXSTS" = "Acquiring XSTS"; diff --git a/Natives/ios_uikit_bridge.m b/Natives/ios_uikit_bridge.m index 89768de761..e0e31b606f 100644 --- a/Natives/ios_uikit_bridge.m +++ b/Natives/ios_uikit_bridge.m @@ -1,9 +1,9 @@ #import "authenticator/BaseAuthenticator.h" #import "AppDelegate.h" #import "SceneDelegate.h" -#import "LoginViewController.h" #import "LauncherNavigationController.h" #import "LauncherPreferences.h" +#import "LauncherSplitViewController.h" #import "SurfaceViewController.h" #include "ios_uikit_bridge.h" @@ -109,7 +109,7 @@ jstring UIKit_accessClipboard(JNIEnv* env, jint action, jstring copySrc) { void UIKit_launchMinecraftSurfaceVC() { // Leave this pref, might be useful later for launching with Quick Actions/Shortcuts/URL Scheme //setPreference(@"internal_launch_on_boot", getPreference(@"restart_before_launch")); - setPreference(@"internal_selected_account", BaseAuthenticator.current.authData[@"username"]); + setPreference(@"selected_account", BaseAuthenticator.current.authData[@"username"]); setPreference(@"internal_useStackQueue", @(isUseStackQueueCall ? YES : NO)); dispatch_async(dispatch_get_main_queue(), ^{ UIWindow *window = currentWindow(); @@ -128,7 +128,10 @@ void launchInitialViewController(UIWindow *window) { if ([getPreference(@"internal_launch_on_boot") boolValue]) { window.rootViewController = [[SurfaceViewController alloc] init]; } else { - LoginViewController *vc = [[LoginViewController alloc] init]; - window.rootViewController = [[UINavigationController alloc] initWithRootViewController:vc]; + if (@available(iOS 14.0, tvOS 14.0, *)) { + window.rootViewController = [[LauncherSplitViewController alloc] initWithStyle:UISplitViewControllerStyleDoubleColumn]; + } else { + window.rootViewController = [[LauncherSplitViewController alloc] init]; + } } } diff --git a/Natives/utils.m b/Natives/utils.m index 88c0d3391d..5833b6713e 100644 --- a/Natives/utils.m +++ b/Natives/utils.m @@ -7,7 +7,19 @@ #include "log.h" #include "utils.h" -BOOL getEntitlementValue(NSString *key); +CFTypeRef SecTaskCopyValueForEntitlement(void* task, NSString* entitlement, CFErrorRef _Nullable *error); +void* SecTaskCreateFromSelf(CFAllocatorRef allocator); + +BOOL getEntitlementValue(NSString *key) { + void *secTask = SecTaskCreateFromSelf(NULL); + CFTypeRef value = SecTaskCopyValueForEntitlement(SecTaskCreateFromSelf(NULL), key, nil); + if (value != nil) { + CFRelease(value); + } + CFRelease(secTask); + + return value != nil && [(__bridge id)value boolValue]; +} BOOL isJITEnabled() { if (getEntitlementValue(@"dynamic-codesigning")) { diff --git a/README.md b/README.md index 7ee45ef256..ed74ed86ef 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ The [PojavLauncher Website](https://pojavlauncherteam.github.io/INSTALL.html#ios Note: This is experimental, although game works smoothly, you should not set Render distance too much. 1. Setup the sideload app -- For iOS 14.0-15.5beta4 on compatible devices, [TrollStore](https://github.com/opa334/TrollStore) is recommended to keep PojavLauncher permanently signed and have JIT enabled by itself. +- For iOS 14.0-15.5beta4, [TrollStore](https://github.com/opa334/TrollStore) is recommended to keep PojavLauncher permanently signed and have JIT enabled by itself. - Otherwise, install [AltStore](https://altstore.io). Others will also work. 2. Download an IPA build of PojavLauncher in the Actions tab. 3. Open the downloaded IPA in the sideload app to install.