Many changes and fixes
Changes: - Decreased multithreaded download max threads to 10 - Moved launcher buttons to match Android UI - Moved UIKit hooking code to separate file - The Launcher tab is now marked as under construction. - Updated README.md to avoid confusion between self-JIT and auto-JIT. New settings section: UI debugging - Unlock iPadOS UI: allow iPhone users to unlock every iPadOS-only features (floating keyboard, popover alert, sheet-styled view controllers, etc.) - Show layout bounds, like the one in Android's developer setting - Show layout overlapping Fixes: - App container data is not shown in the Files app due to `no-container` entitlement, replaced it with `no-sandbox`. - Ignore jinput-platform libraries to avoid download error - Multithreaded assets download breaks - `Send your logs` crashes on iPadOS
This commit is contained in:
1 parent
f404ddd2cb
commit
b806c5df3c
14 files changed
+266
-147
No files matched your search
@@ -91,6 +91,7 @@ add_executable(PojavLauncher
|
||||
log.m
|
||||
JavaLauncher.m
|
||||
external/fishhook/fishhook.c
|
||||
UIKit+hook.m
|
||||
|
||||
authenticator/BaseAuthenticator.m
|
||||
authenticator/LocalAuthenticator.m
|
||||
|
||||
@@ -115,5 +115,7 @@
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>UISupportsDocumentBrowser</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -3,13 +3,17 @@
|
||||
|
||||
NSMutableDictionary *prefDict;
|
||||
NSString* prefPath;
|
||||
// developer debug dict
|
||||
NSMutableDictionary *debugPrefDict;
|
||||
// environment variables dict
|
||||
NSMutableDictionary *envPrefDict;
|
||||
// warnings dict
|
||||
NSMutableDictionary *warnPrefDict;
|
||||
|
||||
id getPreference(NSString* key) {
|
||||
if (!(envPrefDict[key] == [NSNull null] || envPrefDict[key] == nil)) {
|
||||
if (!(debugPrefDict[key] == [NSNull null] || debugPrefDict[key] == nil)) {
|
||||
return debugPrefDict[key];
|
||||
} else if (!(envPrefDict[key] == [NSNull null] || envPrefDict[key] == nil)) {
|
||||
return envPrefDict[key];
|
||||
} else if (!(warnPrefDict[key] == [NSNull null] || warnPrefDict[key] == nil)) {
|
||||
return warnPrefDict[key];
|
||||
@@ -58,7 +62,10 @@ void setDefaultValueForPref(NSMutableDictionary *dict, NSString* key, id value)
|
||||
}
|
||||
|
||||
void setPreference(NSString* key, id value) {
|
||||
if (!(envPrefDict[key] == [NSNull null] || envPrefDict[key] == nil)) {
|
||||
if (!(debugPrefDict[key] == [NSNull null] || debugPrefDict[key] == nil)) {
|
||||
debugPrefDict[key] = value;
|
||||
prefDict[@"debugs"] = debugPrefDict;
|
||||
} else if (!(envPrefDict[key] == [NSNull null] || envPrefDict[key] == nil)) {
|
||||
envPrefDict[key] = value;
|
||||
prefDict[@"env_vars"] = envPrefDict;
|
||||
} else if (!(warnPrefDict[key] == [NSNull null] || warnPrefDict[key] == nil)) {
|
||||
@@ -79,11 +86,17 @@ void fillDefaultWarningDict() {
|
||||
setDefaultValueForPref(warnPrefDict, @"java_warn", @YES);
|
||||
setDefaultValueForPref(warnPrefDict, @"demo_warn", @YES);
|
||||
setDefaultValueForPref(warnPrefDict, @"jb_warn", @YES);
|
||||
setDefaultValueForPref(warnPrefDict, @"customctrl_warn", @YES);
|
||||
setDefaultValueForPref(warnPrefDict, @"int_warn", @YES);
|
||||
setDefaultValueForPref(warnPrefDict, @"auto_ram_warn", @YES);
|
||||
}
|
||||
|
||||
NSMutableDictionary* getDictionarySafe(NSString* key) {
|
||||
if (prefDict[key] == nil) {
|
||||
return [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return prefDict[key];
|
||||
}
|
||||
|
||||
void loadPreferences(BOOL reset) {
|
||||
assert(getenv("POJAV_HOME"));
|
||||
prefPath = [@(getenv("POJAV_HOME"))
|
||||
@@ -91,16 +104,14 @@ void loadPreferences(BOOL reset) {
|
||||
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
prefDict = [NSMutableDictionary dictionaryWithContentsOfFile:prefPath];
|
||||
if (reset || ![fileManager fileExistsAtPath:prefPath]) {
|
||||
if (reset) {
|
||||
[prefDict removeAllObjects];
|
||||
} else if (![fileManager fileExistsAtPath:prefPath]) {
|
||||
prefDict = [[NSMutableDictionary alloc] init];
|
||||
envPrefDict = [[NSMutableDictionary alloc] init];
|
||||
warnPrefDict = [[NSMutableDictionary alloc] init];
|
||||
} else {
|
||||
envPrefDict = prefDict[@"env_vars"];
|
||||
warnPrefDict = prefDict[@"warnings"];
|
||||
}
|
||||
|
||||
assert(prefDict);
|
||||
debugPrefDict = getDictionarySafe(@"debugs");
|
||||
envPrefDict = getDictionarySafe(@"env_vars");
|
||||
warnPrefDict = getDictionarySafe(@"warnings");
|
||||
|
||||
// set default value
|
||||
setDefaultValueForPref(envPrefDict, @"resolution", @(100));
|
||||
@@ -132,9 +143,15 @@ void loadPreferences(BOOL reset) {
|
||||
setDefaultValueForPref(prefDict, @"disable_home_symlink", @YES);
|
||||
}
|
||||
|
||||
// Debug settings
|
||||
setDefaultValueForPref(debugPrefDict, @"debug_ipad_ui", @(UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad));
|
||||
setDefaultValueForPref(debugPrefDict, @"debug_show_layout_bounds", @NO);
|
||||
setDefaultValueForPref(debugPrefDict, @"debug_show_layout_overlap", @NO);
|
||||
|
||||
// Migrate some prefs
|
||||
setPreference(@"java_home", [getPreference(@"java_home") lastPathComponent]);
|
||||
|
||||
prefDict[@"debugs"] = debugPrefDict;
|
||||
prefDict[@"env_vars"] = envPrefDict;
|
||||
prefDict[@"warnings"] = warnPrefDict;
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *);
|
||||
return self.navigationController != nil;
|
||||
};
|
||||
|
||||
self.prefSections = @[@"general", @"video", @"control", @"java"];
|
||||
self.prefSections = @[@"general", @"video", @"control", @"java", @"debug"];
|
||||
|
||||
self.prefContents = @[
|
||||
@[
|
||||
@@ -209,6 +209,28 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *);
|
||||
},
|
||||
@"warnKey": @"mem_warn"
|
||||
}
|
||||
], @[
|
||||
@{@"key": @"debug_ipad_ui",
|
||||
@"hasDetail": @YES,
|
||||
@"icon": @"ipad",
|
||||
@"type": self.typeSwitch,
|
||||
@"enableCondition": whenNotInGame
|
||||
},
|
||||
@{@"key": @"debug_show_layout_bounds",
|
||||
@"hasDetail": @YES,
|
||||
@"icon": @"square.dashed",
|
||||
@"type": self.typeSwitch,
|
||||
@"enableCondition": whenNotInGame,
|
||||
@"requestReload": @YES
|
||||
},
|
||||
@{@"key": @"debug_show_layout_overlap",
|
||||
@"hasDetail": @YES,
|
||||
@"icon": @"square.on.square",
|
||||
@"type": self.typeSwitch,
|
||||
@"enableCondition": ^BOOL(){
|
||||
return [getPreference(@"debug_show_layout_bounds") boolValue] && whenNotInGame();
|
||||
}
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
LauncherMenuViewController *masterVc = [[LauncherMenuViewController alloc] init];
|
||||
UINavigationController *detailVc = [[UINavigationController alloc] init];
|
||||
detailVc.toolbarHidden = NO;
|
||||
|
||||
self.presentsWithGesture = YES;
|
||||
self.preferredDisplayMode = UISplitViewControllerDisplayModeOneBesideSecondary;
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#define AUTORESIZE_MASKS UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin
|
||||
|
||||
@interface LauncherViewController () <UIDocumentPickerDelegate, UIPickerViewDataSource, UIPickerViewDelegate, UIPopoverPresentationControllerDelegate> {
|
||||
}
|
||||
|
||||
@@ -29,28 +31,25 @@ int versionSelectedAt = 0;
|
||||
[self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
|
||||
[self setNeedsUpdateOfHomeIndicatorAutoHidden];
|
||||
|
||||
CGRect screenBounds = self.view.frame;
|
||||
|
||||
int width = (int) roundf(screenBounds.size.width);
|
||||
int height = (int) roundf(screenBounds.size.height) - self.navigationController.navigationBar.frame.size.height;
|
||||
UIToolbar *toolbar = self.navigationController.toolbar;
|
||||
CGFloat splitWidth = toolbar.frame.size.width / 3;
|
||||
|
||||
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
|
||||
[self.view addSubview:scrollView];
|
||||
scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
|
||||
|
||||
UILabel *versionTextView = [[UILabel alloc] initWithFrame:CGRectMake(4.0, 4.0, 0.0, 0.0)];
|
||||
versionTextView.text = @"Minecraft version: ";
|
||||
versionTextView.text = @"[under construction]";
|
||||
versionTextView.numberOfLines = 0;
|
||||
[versionTextView sizeToFit];
|
||||
[scrollView addSubview:versionTextView];
|
||||
|
||||
versionTextField = [[UITextField alloc] initWithFrame:CGRectMake(versionTextView.bounds.size.width + 4.0, 4.0, width - versionTextView.bounds.size.width - 8.0, height - 58.0)];
|
||||
versionTextField = [[UITextField alloc] initWithFrame:CGRectMake(0, 4, splitWidth, toolbar.frame.size.height/2 - 4)];
|
||||
[versionTextField addTarget:versionTextField action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
|
||||
versionTextField.autoresizingMask = AUTORESIZE_MASKS;
|
||||
versionTextField.placeholder = @"Specify version...";
|
||||
versionTextField.text = (NSString *) getPreference(@"selected_version");
|
||||
versionTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentTop;
|
||||
versionTextField.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
|
||||
|
||||
versionTextField.textAlignment = NSTextAlignmentCenter;
|
||||
|
||||
self.versionList = [[NSMutableArray alloc] init];
|
||||
versionPickerView = [[UIPickerView alloc] init];
|
||||
@@ -75,40 +74,47 @@ int versionSelectedAt = 0;
|
||||
versionTextField.inputAccessoryView = versionPickToolbar;
|
||||
versionTextField.inputView = versionPickerView;
|
||||
|
||||
[scrollView addSubview:versionTextField];
|
||||
[toolbar addSubview:versionTextField];
|
||||
|
||||
if (@available(iOS 14.0, *)) {
|
||||
// use UIMenu
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[[UIImage systemImageNamed:@"ellipsis.circle"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] style:UIBarButtonItemStyleDone target:self action:@selector(displayOptions:)];
|
||||
UIAction *option1 = [UIAction actionWithTitle:@"Launch a mod installer" image:[[UIImage systemImageNamed:@"internaldrive"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil
|
||||
handler:^(__kindof UIAction * _Nonnull action) {[self enterModInstaller:self.navigationItem.rightBarButtonItem];}];
|
||||
UIAction *option2 = [UIAction actionWithTitle:@"Custom controls" image:[[UIImage systemImageNamed:@"dpad.right.fill"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil
|
||||
handler:^(__kindof UIAction * _Nonnull action) {[self enterCustomControls];}];
|
||||
UIMenu *menu = [UIMenu menuWithTitle:@"" image:nil identifier:nil
|
||||
options:UIMenuOptionsDisplayInline children:@[option1, option2]];
|
||||
self.navigationItem.rightBarButtonItem.action = nil;
|
||||
self.navigationItem.rightBarButtonItem.primaryAction = nil;
|
||||
self.navigationItem.rightBarButtonItem.menu = menu;
|
||||
} else {
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Options" style:UIBarButtonItemStyleDone target:self action:@selector(displayOptions:)];
|
||||
}
|
||||
self.progressViewMain = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 0, toolbar.frame.size.width, 4.0)];
|
||||
self.progressViewSub = [[UIProgressView alloc] initWithFrame:CGRectMake(0, toolbar.frame.size.height - 4.0, toolbar.frame.size.width, 4.0)];
|
||||
self.progressViewMain.autoresizingMask = self.progressViewSub.autoresizingMask = AUTORESIZE_MASKS;
|
||||
self.progressViewMain.hidden = self.progressViewSub.hidden = YES;
|
||||
[toolbar addSubview:self.progressViewMain];
|
||||
[toolbar addSubview:self.progressViewSub];
|
||||
|
||||
self.progressViewMain = [[UIProgressView alloc] initWithFrame:CGRectMake(4.0, height - 58.0, width - 8.0, 6.0)];
|
||||
self.progressViewSub = [[UIProgressView alloc] initWithFrame:CGRectMake(4.0, height - 58.0, width - 8.0, 6.0)];
|
||||
self.progressViewMain.alpha = 0.6;
|
||||
self.progressViewSub.alpha = 0.4;
|
||||
[scrollView addSubview:self.progressViewSub];
|
||||
[scrollView addSubview:self.progressViewMain];
|
||||
|
||||
self.buttonInstall = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
self.buttonInstall = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
self.buttonInstall.enabled = NO;
|
||||
[self.buttonInstall setTitle:@"Play" forState:UIControlStateNormal];
|
||||
self.buttonInstall.frame = CGRectMake(10.0, height - 54.0, 100.0, 50.0);
|
||||
[self.buttonInstall setTitle:NSLocalizedString(@"Play", nil) forState:UIControlStateNormal];
|
||||
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(splitWidth, 6.0, splitWidth, toolbar.frame.size.height - 12.0);
|
||||
self.buttonInstall.tintColor = UIColor.whiteColor;
|
||||
[self.buttonInstall addTarget:self action:@selector(launchMinecraft:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[scrollView addSubview:self.buttonInstall];
|
||||
|
||||
self.progressText = [[UILabel alloc] initWithFrame:CGRectMake(120.0, height - 54.0, width - 124.0, 50.0)];
|
||||
[scrollView addSubview:self.progressText];
|
||||
[toolbar addSubview:self.buttonInstall];
|
||||
|
||||
UIButton *buttonCustomControls = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
[buttonCustomControls setTitle:NSLocalizedString(@"launcher.menu.custom_controls", nil) forState:UIControlStateNormal];
|
||||
buttonCustomControls.autoresizingMask = AUTORESIZE_MASKS;
|
||||
buttonCustomControls.frame = CGRectMake(0, toolbar.frame.size.height/2, splitWidth, toolbar.frame.size.height/2);
|
||||
[buttonCustomControls addTarget:self action:@selector(enterCustomControls) forControlEvents:UIControlEventTouchUpInside];
|
||||
[toolbar addSubview:buttonCustomControls];
|
||||
|
||||
UIButton *buttonInstallJar = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
[buttonInstallJar setTitle:NSLocalizedString(@"launcher.menu.install_jar", nil) forState:UIControlStateNormal];
|
||||
buttonInstallJar.autoresizingMask = AUTORESIZE_MASKS;
|
||||
buttonInstallJar.frame = CGRectMake(splitWidth*2, toolbar.frame.size.height/2, splitWidth, toolbar.frame.size.height/2);
|
||||
[buttonInstallJar addTarget:self action:@selector(enterModInstaller) forControlEvents:UIControlEventTouchUpInside];
|
||||
[toolbar addSubview:buttonInstallJar];
|
||||
|
||||
self.progressText = [[UILabel alloc] initWithFrame:CGRectMake(0, 4, toolbar.frame.size.width, 20)];
|
||||
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;
|
||||
[toolbar addSubview:self.progressText];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
@@ -216,45 +222,14 @@ int versionSelectedAt = 0;
|
||||
[self reloadVersionList:sender.selectedSegmentIndex];
|
||||
}
|
||||
|
||||
#pragma mark - Button click events
|
||||
- (void)displayOptions:(UIBarButtonItem*)sender {
|
||||
if (@available(iOS 14.0, *)) {
|
||||
// use UIMenu
|
||||
} else {
|
||||
// use UIAlertController
|
||||
UIAlertController *fullAlert = [UIAlertController alertControllerWithTitle:@"Options" message:@"" preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
UIAlertAction *option1 = [UIAlertAction actionWithTitle:@"Launch a mod installer" style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * _Nonnull action) {[self enterModInstaller:self.navigationItem.rightBarButtonItem];}];
|
||||
UIAlertAction *option2 = [UIAlertAction actionWithTitle:@"Custom controls" style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * _Nonnull action) {[self enterCustomControls];}];
|
||||
fullAlert.popoverPresentationController.barButtonItem = sender;
|
||||
[self presentViewController:fullAlert animated:YES completion:nil];
|
||||
[fullAlert addAction:option1];
|
||||
[fullAlert addAction:option2];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Options
|
||||
- (void)enterCustomControls {
|
||||
if (![getPreference(@"customctrl_warn") boolValue]) {
|
||||
CustomControlsViewController *vc = [[CustomControlsViewController alloc] init];
|
||||
vc.modalPresentationStyle = UIModalPresentationOverFullScreen;
|
||||
[self presentViewController:vc animated:YES completion:nil];
|
||||
return;
|
||||
}
|
||||
setPreference(@"customctrl_warn", @(NO));
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Warning" message:@"This option is unfinished, some might be incomplete or missing." preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
if (alert.popoverPresentationController != nil) {
|
||||
alert.popoverPresentationController.sourceView = self.view;
|
||||
alert.popoverPresentationController.sourceRect = CGRectMake(self.view.frame.size.width-10.0, 0, 10, 10);
|
||||
}
|
||||
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
|
||||
[self enterCustomControls];
|
||||
}];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
[alert addAction:ok];
|
||||
CustomControlsViewController *vc = [[CustomControlsViewController alloc] init];
|
||||
vc.modalPresentationStyle = UIModalPresentationOverFullScreen;
|
||||
[self presentViewController:vc animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)enterModInstaller:(UIBarButtonItem*)sender {
|
||||
- (void)enterModInstaller {
|
||||
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"com.sun.java-archive"]
|
||||
inMode:UIDocumentPickerModeImport];
|
||||
documentPicker.delegate = self;
|
||||
@@ -277,7 +252,9 @@ int versionSelectedAt = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
sender.alpha = 0.5;
|
||||
sender.enabled = NO;
|
||||
self.progressViewMain.hidden = self.progressViewSub.hidden = NO;
|
||||
|
||||
NSObject *object = [self.versionList objectAtIndex:[versionPickerView selectedRowInComponent:0]];
|
||||
|
||||
@@ -288,7 +265,10 @@ int versionSelectedAt = 0;
|
||||
self.progressViewMain.observedProgress = mainProgress;
|
||||
self.progressViewSub.observedProgress = progress;
|
||||
if (stage == nil) {
|
||||
sender.alpha = 1;
|
||||
sender.enabled = YES;
|
||||
self.progressText.text = nil;
|
||||
self.progressViewMain.hidden = self.progressViewSub.hidden = YES;
|
||||
if (mainProgress != nil) {
|
||||
UIKit_launchMinecraftSurfaceVC();
|
||||
}
|
||||
|
||||
@@ -24,15 +24,10 @@
|
||||
extern NSMutableDictionary *prefDict;
|
||||
|
||||
#pragma mark - LoginViewController
|
||||
@interface LoginViewController () <ASWebAuthenticationPresentationContextProviding>{
|
||||
}
|
||||
@property (nonatomic, strong) ASWebAuthenticationSession *authVC;
|
||||
@property (nonatomic, strong) UIActivityViewController *activityViewController;
|
||||
@interface LoginViewController () <ASWebAuthenticationPresentationContextProviding> {}
|
||||
@end
|
||||
|
||||
@implementation LoginViewController
|
||||
@synthesize authVC;
|
||||
@synthesize activityViewController;
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
@@ -268,7 +263,7 @@ extern NSMutableDictionary *prefDict;
|
||||
- (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"];
|
||||
|
||||
authVC =
|
||||
ASWebAuthenticationSession *authVC =
|
||||
[[ASWebAuthenticationSession alloc] initWithURL:url
|
||||
callbackURLScheme:@"ms-xal-00000000402b5328"
|
||||
completionHandler:^(NSURL * _Nullable callbackURL,
|
||||
@@ -434,7 +429,8 @@ extern NSMutableDictionary *prefDict;
|
||||
{
|
||||
NSString *latestlogPath = [NSString stringWithFormat:@"file://%s/latestlog.old.txt", getenv("POJAV_HOME")];
|
||||
NSLog(@"Path is %@", latestlogPath);
|
||||
activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[@"latestlog.txt", [NSURL URLWithString:latestlogPath]] applicationActivities:nil];
|
||||
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[@"latestlog.txt", [NSURL URLWithString:latestlogPath]] applicationActivities:nil];
|
||||
activityViewController.popoverPresentationController.barButtonItem = self.navigationItem.rightBarButtonItems[0];
|
||||
|
||||
[self presentViewController:activityViewController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
@@ -283,6 +283,7 @@ static AFURLSessionManager* manager;
|
||||
library[@"skip"] = @(
|
||||
// Exclude platform-dependant libraries
|
||||
library[@"downloads"][@"classifiers"] != nil ||
|
||||
library[@"natives"] != nil ||
|
||||
// Exclude LWJGL libraries
|
||||
[library[@"name"] hasPrefix:@"org.lwjgl"]
|
||||
);
|
||||
@@ -379,12 +380,15 @@ static AFURLSessionManager* manager;
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
int downloadIndex = -1;
|
||||
__block int jobsAvailable = 20;
|
||||
__block int jobsAvailable = 10;
|
||||
for (NSString *name in assets[@"objects"]) {
|
||||
if (jobsAvailable < 0) {
|
||||
break;
|
||||
} else if (jobsAvailable == 0) {
|
||||
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
||||
//dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
||||
while (jobsAvailable == 0) {
|
||||
usleep(50000);
|
||||
}
|
||||
}
|
||||
|
||||
NSString *hash = assets[@"objects"][name][@"hash"];
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <objc/runtime.h>
|
||||
#import "LauncherPreferences.h"
|
||||
|
||||
void swizzle(Class class, SEL originalAction, SEL swizzledAction) {
|
||||
method_exchangeImplementations(class_getInstanceMethod(class, originalAction), class_getInstanceMethod(class, swizzledAction));
|
||||
}
|
||||
|
||||
__attribute__((constructor)) void hookConstructor(void) {
|
||||
swizzle(UIDevice.class, @selector(userInterfaceIdiom), @selector(hook_userInterfaceIdiom));
|
||||
swizzle(UIToolbar.class, @selector(sizeThatFits:), @selector(hook_sizeThatFits:));
|
||||
swizzle(UIView.class, @selector(didMoveToSuperview), @selector(hook_didMoveToSuperview));
|
||||
swizzle(UIView.class, @selector(setFrame:), @selector(hook_setFrame:));
|
||||
}
|
||||
|
||||
@implementation UIDevice(hook)
|
||||
|
||||
- (UIUserInterfaceIdiom)hook_userInterfaceIdiom {
|
||||
id value = getPreference(@"debug_ipad_ui");
|
||||
if (value == nil) {
|
||||
return self.hook_userInterfaceIdiom;
|
||||
}
|
||||
return [value boolValue] ? UIUserInterfaceIdiomPad : UIUserInterfaceIdiomPhone;
|
||||
}
|
||||
|
||||
@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 UIView(hook)
|
||||
const NSString *cornerLayerKey = @"cornerLayer";
|
||||
|
||||
- (void)updateCornerLayer {
|
||||
CAShapeLayer *cornerLayer = objc_getAssociatedObject(self, &cornerLayerKey);
|
||||
NSNumber *cornerWidth = @5;
|
||||
NSNumber *cornerSpaceW = @(self.frame.size.width-10);
|
||||
NSNumber *cornerSpaceH = @(self.frame.size.height-10);
|
||||
cornerLayer.lineDashPattern = @[cornerWidth,cornerSpaceW,cornerWidth,@0,cornerWidth,cornerSpaceH,cornerWidth,@0,cornerWidth,cornerSpaceW,cornerWidth,@0,cornerWidth,cornerSpaceH,cornerWidth];
|
||||
cornerLayer.path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)].CGPath;
|
||||
if ([getPreference(@"debug_show_layout_overlap") boolValue]) {
|
||||
cornerLayer.fillColor = [UIColor.yellowColor colorWithAlphaComponent:0.1].CGColor;
|
||||
} else {
|
||||
cornerLayer.fillColor = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)hook_didMoveToSuperview {
|
||||
if ([getPreference(@"debug_show_layout_bounds") boolValue]) {
|
||||
self.layer.borderWidth = 1;
|
||||
self.layer.borderColor = UIColor.redColor.CGColor;
|
||||
if (self.layer.sublayers.count == 0) {
|
||||
CAShapeLayer *cornerLayer = [CAShapeLayer layer];
|
||||
cornerLayer.strokeColor = UIColor.blueColor.CGColor;
|
||||
cornerLayer.lineWidth = 1;
|
||||
[self.layer addSublayer:cornerLayer];
|
||||
objc_setAssociatedObject(self, &cornerLayerKey, cornerLayer, OBJC_ASSOCIATION_ASSIGN);
|
||||
[self updateCornerLayer];
|
||||
}
|
||||
}
|
||||
[self hook_didMoveToSuperview];
|
||||
}
|
||||
|
||||
- (void)hook_setFrame:(CGRect)frame {
|
||||
[self hook_setFrame:frame];
|
||||
[self updateCornerLayer];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIWindow(hook)
|
||||
|
||||
// Simulate safe area on iPhones without notch
|
||||
/*
|
||||
- (UIEdgeInsets)safeAreaInsets {
|
||||
return UIEdgeInsetsMake(0, 44, 21, 44);
|
||||
}
|
||||
*/
|
||||
|
||||
- (UIViewController *)visibleViewController {
|
||||
UIViewController *current = self.rootViewController;
|
||||
while (current.presentedViewController) {
|
||||
if ([current.presentedViewController isKindOfClass:UIAlertController.class] || [current.presentedViewController isKindOfClass:NSClassFromString(@"UIInputWindowController")]) {
|
||||
break;
|
||||
}
|
||||
current = current.presentedViewController;
|
||||
}
|
||||
if ([current isKindOfClass:UINavigationController.class]) {
|
||||
return [(UINavigationController *)self.rootViewController visibleViewController];
|
||||
} else {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
// This forces the navigation bar to keep its height (44dp) in landscape
|
||||
@implementation UINavigationBar(forceFullHeightInLandscape)
|
||||
- (BOOL)forceFullHeightInLandscape {
|
||||
return YES;
|
||||
//UIScreen.mainScreen.traitCollection.userInterfaceIdiom == UIUserInterfaceIdiomPhone;
|
||||
}
|
||||
@end
|
||||
|
||||
UIWindow* currentWindow() {
|
||||
id delegate = UIApplication.sharedApplication.delegate;
|
||||
if (@available(iOS 13.0, *)) {
|
||||
delegate = UIApplication.sharedApplication.connectedScenes.anyObject.delegate;
|
||||
}
|
||||
return [delegate window];
|
||||
}
|
||||
|
||||
UIViewController* currentVC() {
|
||||
return currentWindow().visibleViewController;
|
||||
}
|
||||
@@ -8,6 +8,12 @@
|
||||
"game.menu.log_output" = "Log output";
|
||||
"game.menu.custom_controls" = "Custom controls";
|
||||
|
||||
// The options button at the top-right corner on iOS 12
|
||||
"Options" = "Options";
|
||||
|
||||
// The play button
|
||||
"Play" = "Play";
|
||||
|
||||
// Version categories
|
||||
"Installed" = "Installed";
|
||||
"Snapshot" = "Snapshot";
|
||||
@@ -17,6 +23,9 @@
|
||||
|
||||
"init.migrateDir" = "The PojavLauncher data directory is now %@. All of your existing data has been moved to the new location.";
|
||||
|
||||
"launcher.menu.custom_controls" = "Custom controls";
|
||||
"launcher.menu.install_jar" = "Install .jar";
|
||||
|
||||
"login.error.username.outOfRange" = "Username must be at least 3 characters and maximum 16 characters";
|
||||
|
||||
"login.alert.field.username" = "Username";
|
||||
@@ -80,6 +89,8 @@
|
||||
"preference.section.video" = "Video Settings";
|
||||
"preference.section.footer.video" = "Auto renderer allows Minecraft to decide which renderer to use. Decreasing resolution reduces GPU workload for better performance.";
|
||||
"preference.section.control" = "Control customization";
|
||||
"preference.section.debug" = "UI Debugging settings";
|
||||
"preference.section.footer.debug" = "Aside from debugging, these settings may be useful for custom controls. You may need to restart the launcher for changes to take effect.";
|
||||
//"preference.section.footer.control" = "";
|
||||
"preference.section.java" = "Java Tweaks";
|
||||
//"preference.section.footer.java" = "";
|
||||
@@ -141,3 +152,10 @@
|
||||
"preference.warn.allocated_memory" = "Due to limitations in the operating system itself, this option may cause instability at this and higher values. Proceed with caution.";
|
||||
"preference.title.custom_env" = "Environment variables";
|
||||
"preference.detail.custom_env" = "This option is under construction.";
|
||||
|
||||
"preference.title.debug_ipad_ui" = "Unlock iPadOS UI";
|
||||
"preference.detail.debug_ipad_ui" = "Unlock iPad-exlusive UI (alert, keyboard, etc.) if enabled or iPhone UI if disabled.";
|
||||
"preference.title.debug_show_layout_bounds" = "Show layout bounds";
|
||||
"preference.detail.debug_show_layout_bounds" = "Show clip bounds, margins";
|
||||
"preference.title.debug_show_layout_overlap" = "Show layout overlapping";
|
||||
"preference.detail.debug_show_layout_overlap" = "Overlay every view with a transparent color to reveal overlapping views.";
|
||||
+2
-2
@@ -124,7 +124,7 @@ void init_logDeviceAndVer(char *argument) {
|
||||
regLog("[Pre-init] Entitlements availability:");
|
||||
printEntitlementAvailability(@"com.apple.developer.kernel.extended-virtual-addressing");
|
||||
printEntitlementAvailability(@"com.apple.developer.kernel.increased-memory-limit");
|
||||
printEntitlementAvailability(@"com.apple.private.security.no-container");
|
||||
printEntitlementAvailability(@"com.apple.private.security.no-sandbox");
|
||||
printEntitlementAvailability(@"dynamic-codesigning");
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ int main(int argc, char * argv[]) {
|
||||
init_migrateToPlist("java_args", "overrideargs.txt");
|
||||
|
||||
// If sandbox is disabled, W^X JIT can be enabled by PojavLauncher itself
|
||||
if (getEntitlementValue(@"com.apple.private.security.no-container")) {
|
||||
if (getEntitlementValue(@"com.apple.private.security.no-sandbox")) {
|
||||
NSLog(@"[Pre-init] Sandbox is disabled, trying to enable JIT");
|
||||
int pid;
|
||||
int ret = posix_spawnp(&pid, argv[0], NULL, NULL, argv, environ);
|
||||
|
||||
@@ -9,52 +9,6 @@
|
||||
|
||||
BOOL getEntitlementValue(NSString *key);
|
||||
|
||||
@implementation UIWindow(ext)
|
||||
|
||||
// Simulate safe area on iPhones without notch
|
||||
/*
|
||||
- (UIEdgeInsets)safeAreaInsets {
|
||||
return UIEdgeInsetsMake(0, 44, 21, 44);
|
||||
}
|
||||
*/
|
||||
|
||||
- (UIViewController *)visibleViewController {
|
||||
UIViewController *current = self.rootViewController;
|
||||
while (current.presentedViewController) {
|
||||
if ([current.presentedViewController isKindOfClass:UIAlertController.class] || [current.presentedViewController isKindOfClass:NSClassFromString(@"UIInputWindowController")]) {
|
||||
break;
|
||||
}
|
||||
current = current.presentedViewController;
|
||||
}
|
||||
if ([current isKindOfClass:UINavigationController.class]) {
|
||||
return [(UINavigationController *)self.rootViewController visibleViewController];
|
||||
} else {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
// This forces the navigation bar to keep its height (44dp) in landscape
|
||||
@implementation UINavigationBar(forceFullHeightInLandscape)
|
||||
- (BOOL)forceFullHeightInLandscape {
|
||||
return UIScreen.mainScreen.traitCollection.userInterfaceIdiom == UIUserInterfaceIdiomPhone;
|
||||
}
|
||||
@end
|
||||
|
||||
UIWindow* currentWindow() {
|
||||
id delegate = UIApplication.sharedApplication.delegate;
|
||||
if (@available(iOS 13.0, *)) {
|
||||
delegate = UIApplication.sharedApplication.connectedScenes.anyObject.delegate;
|
||||
}
|
||||
return [delegate window];
|
||||
}
|
||||
|
||||
UIViewController* currentVC() {
|
||||
return currentWindow().visibleViewController;
|
||||
}
|
||||
|
||||
BOOL isJITEnabled() {
|
||||
if (getEntitlementValue(@"dynamic-codesigning")) {
|
||||
return YES;
|
||||
|
||||
@@ -25,7 +25,7 @@ The [PojavLauncher Website](https://pojav.ml) has extensive documentation on how
|
||||
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 automatically enabled.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<true/>
|
||||
|
||||
<!-- Disable sandbox so that PojavLauncher can enable JIT by itself -->
|
||||
<key>com.apple.private.security.no-container</key>
|
||||
<key>com.apple.private.security.no-sandbox</key>
|
||||
<true/>
|
||||
|
||||
<!-- The following entitlements are required if sandbox is disabled -->
|
||||
|
||||
Reference in new issue
Block a user