222 lines
9.4 KiB
Objective-C
222 lines
9.4 KiB
Objective-C
#import "AFNetworking.h"
|
|
#import "ForgeInstallViewController.h"
|
|
#import "LauncherNavigationController.h"
|
|
#import "WFWorkflowProgressView.h"
|
|
#import "ios_uikit_bridge.h"
|
|
#import "utils.h"
|
|
#include <dlfcn.h>
|
|
|
|
@interface ForgeInstallViewController()<NSXMLParserDelegate>
|
|
@property(atomic) AFURLSessionManager *afManager;
|
|
@property(nonatomic) WFWorkflowProgressView *progressView;
|
|
|
|
@property(nonatomic) NSDictionary *endpoints;
|
|
@property(nonatomic) NSMutableArray<NSNumber *> *visibilityList;
|
|
@property(nonatomic) NSMutableArray<NSString *> *versionList;
|
|
@property(nonatomic) NSMutableArray<NSMutableArray *> *forgeList;
|
|
@property(nonatomic, assign) BOOL isVersionElement;
|
|
@end
|
|
|
|
@implementation ForgeInstallViewController
|
|
|
|
- (void)viewDidLoad {
|
|
[super viewDidLoad];
|
|
UISegmentedControl *segment = [[UISegmentedControl alloc] initWithItems:@[@"Forge", @"NeoForge"]];
|
|
segment.selectedSegmentIndex = 0;
|
|
[segment addTarget:self action:@selector(segmentChanged:) forControlEvents:UIControlEventValueChanged];
|
|
self.navigationItem.titleView = segment;
|
|
|
|
// Load WFWorkflowProgressView
|
|
dlopen("/System/Library/PrivateFrameworks/WorkflowUIServices.framework/WorkflowUIServices", RTLD_GLOBAL);
|
|
self.progressView = [[NSClassFromString(@"WFWorkflowProgressView") alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
|
|
self.progressView.resolvedTintColor = self.view.tintColor;
|
|
[self.progressView addTarget:self
|
|
action:@selector(actionCancelDownload) forControlEvents:UIControlEventTouchUpInside];
|
|
|
|
self.endpoints = @{
|
|
@"Forge": @{
|
|
@"installer": @"https://maven.minecraftforge.net/net/minecraftforge/forge/%1$@/forge-%[email protected]",
|
|
@"metadata": @"https://maven.minecraftforge.net/net/minecraftforge/forge/maven-metadata.xml"
|
|
},
|
|
@"NeoForge": @{
|
|
@"installer": @"https://maven.neoforged.net/net/neoforged/forge/%1$@/forge-%[email protected]",
|
|
@"metadata": @"https://maven.neoforged.net/releases/net/neoforged/forge/maven-metadata.xml"
|
|
}
|
|
};
|
|
self.visibilityList = [NSMutableArray new];
|
|
self.versionList = [NSMutableArray new];
|
|
self.forgeList = [NSMutableArray new];
|
|
[self loadMetadataFromVendor:@"Forge"];
|
|
}
|
|
|
|
- (void)actionCancelDownload {
|
|
[self.afManager invalidateSessionCancelingTasks:YES resetSession:NO];
|
|
}
|
|
|
|
- (void)actionClose {
|
|
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
|
}
|
|
|
|
- (void)loadMetadataFromVendor:(NSString *)vendor {
|
|
[self switchToLoadingState];
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
NSURL *url = [[NSURL alloc] initWithString:self.endpoints[vendor][@"metadata"]];
|
|
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
|
|
parser.delegate = self;
|
|
if (![parser parse]) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
showDialog(localize(@"Error", nil), parser.parserError.localizedDescription);
|
|
[self actionClose];
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
- (void)switchToLoadingState {
|
|
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleMedium];
|
|
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:indicator];
|
|
[indicator startAnimating];
|
|
self.navigationController.modalInPresentation = YES;
|
|
}
|
|
|
|
- (void)switchToReadyState {
|
|
UIActivityIndicatorView *indicator = (id)self.navigationItem.rightBarButtonItem.customView;
|
|
[indicator stopAnimating];
|
|
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemClose target:self action:@selector(actionClose)];
|
|
self.navigationController.modalInPresentation = NO;
|
|
}
|
|
|
|
- (void)segmentChanged:(UISegmentedControl *)segment {
|
|
[self.visibilityList removeAllObjects];
|
|
[self.versionList removeAllObjects];
|
|
[self.forgeList removeAllObjects];
|
|
[self.tableView reloadData];
|
|
NSString *vendor = [segment titleForSegmentAtIndex:segment.selectedSegmentIndex];
|
|
[self loadMetadataFromVendor:vendor];
|
|
}
|
|
|
|
#pragma mark UITableViewDataSource
|
|
|
|
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
|
return self.versionList.count;
|
|
}
|
|
|
|
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
|
|
UITableViewHeaderFooterView *view = [self.tableView dequeueReusableHeaderFooterViewWithIdentifier:@"section"];
|
|
if (!view) {
|
|
view = [[UITableViewHeaderFooterView alloc] initWithReuseIdentifier:@"section"];
|
|
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tableViewDidSelectSection:)];
|
|
[view addGestureRecognizer:tapGesture];
|
|
}
|
|
return view;
|
|
}
|
|
|
|
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
|
|
return self.versionList[section];
|
|
}
|
|
|
|
- (void)tableViewDidSelectSection:(UITapGestureRecognizer *)sender {
|
|
UITableViewHeaderFooterView *view = (id)sender.view;
|
|
int section = [self.versionList indexOfObject:view.textLabel.text];
|
|
self.visibilityList[section] = @(!self.visibilityList[section].boolValue);
|
|
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationAutomatic];
|
|
}
|
|
|
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
|
return self.visibilityList[section].boolValue ? self.forgeList[section].count : 0;
|
|
}
|
|
|
|
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
|
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
|
|
if (!cell) {
|
|
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
|
|
}
|
|
|
|
cell.textLabel.text = self.forgeList[indexPath.section][indexPath.row];
|
|
return cell;
|
|
}
|
|
|
|
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
|
[tableView deselectRowAtIndexPath:indexPath animated:NO];
|
|
tableView.allowsSelection = NO;
|
|
|
|
[self switchToLoadingState];
|
|
self.progressView.fractionCompleted = 0;
|
|
|
|
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
|
|
cell.accessoryView = self.progressView;
|
|
|
|
UISegmentedControl *segment = (id)self.navigationItem.titleView;
|
|
NSString *vendor = [segment titleForSegmentAtIndex:segment.selectedSegmentIndex];
|
|
NSString *jarURL = [NSString stringWithFormat:self.endpoints[vendor][@"installer"], cell.textLabel.text];
|
|
NSString *outPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp.jar"];
|
|
NSDebugLog(@"[Forge Installer] Downloading %@", jarURL);
|
|
|
|
self.afManager = [AFURLSessionManager new];
|
|
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:jarURL]];
|
|
NSURLSessionDownloadTask *downloadTask = [self.afManager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull progress){
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
self.progressView.fractionCompleted = progress.fractionCompleted;
|
|
});
|
|
} destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
|
|
[NSFileManager.defaultManager removeItemAtPath:outPath error:nil];
|
|
return [NSURL fileURLWithPath:outPath];
|
|
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
tableView.allowsSelection = YES;
|
|
cell.accessoryView = nil;
|
|
if (error) {
|
|
if (error.code != NSURLErrorCancelled) {
|
|
NSDebugLog(@"Error: %@", error);
|
|
showDialog(localize(@"Error", nil), error.localizedDescription);
|
|
}
|
|
[self switchToReadyState];
|
|
return;
|
|
}
|
|
LauncherNavigationController *navVC = (id)((UISplitViewController *)self.presentingViewController).viewControllers[1];
|
|
[self dismissViewControllerAnimated:YES completion:^{
|
|
[navVC enterModInstallerWithPath:outPath hitEnterAfterWindowShown:YES];
|
|
}];
|
|
});
|
|
}];
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
[downloadTask resume];
|
|
});
|
|
}
|
|
|
|
- (void)addVersionToList:(NSString *)version {
|
|
if (![version containsString:@"-"]) {
|
|
return;
|
|
}
|
|
NSRange range = [version rangeOfString:@"-"];
|
|
NSString *gameVersion = [version substringToIndex:range.location];
|
|
//NSString *forgeVersion = [version substringFromIndex:range.location + 1];
|
|
if (![self.versionList containsObject:gameVersion]) {
|
|
[self.visibilityList addObject:@(NO)];
|
|
[self.versionList addObject:gameVersion];
|
|
[self.forgeList addObject:[NSMutableArray new]];
|
|
}
|
|
[self.forgeList.lastObject addObject:version];
|
|
}
|
|
|
|
#pragma mark NSXMLParser
|
|
|
|
- (void)parserDidEndDocument:(NSXMLParser *)unused {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self switchToReadyState];
|
|
[self.tableView reloadData];
|
|
});
|
|
}
|
|
|
|
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
|
|
self.isVersionElement = [elementName isEqualToString:@"version"];
|
|
}
|
|
|
|
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)version {
|
|
if (self.isVersionElement) {
|
|
[self addVersionToList:version];
|
|
}
|
|
}
|
|
|
|
@end
|