Files

153 lines
6.6 KiB
Objective-C

#import "MinecraftResourceDownloadTask.h"
#import "ModrinthAPI.h"
#import "PLProfiles.h"
@implementation ModrinthAPI
- (instancetype)init {
return [super initWithURL:@"https://api.modrinth.com/v2"];
}
- (NSMutableArray *)searchModWithFilters:(NSDictionary<NSString *, NSString *> *)searchFilters previousPageResult:(NSMutableArray *)modrinthSearchResult {
int limit = 50;
NSMutableString *facetString = [NSMutableString new];
[facetString appendString:@"["];
[facetString appendFormat:@"[\"project_type:%@\"]", searchFilters[@"isModpack"].boolValue ? @"modpack" : @"mod"];
if (searchFilters[@"mcVersion"].length > 0) {
[facetString appendFormat:@",[\"versions:%@\"]", searchFilters[@"mcVersion"]];
}
[facetString appendString:@"]"];
NSDictionary *params = @{
@"facets": facetString,
@"query": [searchFilters[@"name"] stringByReplacingOccurrencesOfString:@" " withString:@"+"],
@"limit": @(limit),
@"index": @"relevance",
@"offset": @(modrinthSearchResult.count)
};
NSDictionary *response = [self getEndpoint:@"search" params:params];
if (!response) {
return nil;
}
NSMutableArray *result = modrinthSearchResult ?: [NSMutableArray new];
for (NSDictionary *hit in response[@"hits"]) {
BOOL isModpack = [hit[@"project_type"] isEqualToString:@"modpack"];
[result addObject:@{
@"apiSource": @(1), // Constant MODRINTH
@"isModpack": @(isModpack),
@"id": hit[@"project_id"],
@"title": hit[@"title"],
@"description": hit[@"description"],
@"imageUrl": hit[@"icon_url"]
}.mutableCopy];
}
self.reachedLastPage = result.count >= [response[@"total_hits"] unsignedLongValue];
return result;
}
- (void)loadDetailsOfMod:(NSMutableDictionary *)item {
NSArray *response = [self getEndpoint:[NSString stringWithFormat:@"project/%@/version", item[@"id"]] params:nil];
if (!response) {
return;
}
NSArray<NSString *> *names = [response valueForKey:@"name"];
NSMutableArray<NSString *> *mcNames = [NSMutableArray new];
NSMutableArray<NSString *> *urls = [NSMutableArray new];
NSMutableArray<NSString *> *hashes = [NSMutableArray new];
NSMutableArray<NSString *> *sizes = [NSMutableArray new];
[response enumerateObjectsUsingBlock:
^(NSDictionary *version, NSUInteger i, BOOL *stop) {
NSDictionary *file = [version[@"files"] firstObject];
mcNames[i] = [version[@"game_versions"] firstObject];
sizes[i] = file[@"size"];
urls[i] = file[@"url"];
NSDictionary *hashesMap = file[@"hashes"];
hashes[i] = hashesMap[@"sha1"] ?: [NSNull null];
}];
item[@"versionNames"] = names;
item[@"mcVersionNames"] = mcNames;
item[@"versionSizes"] = sizes;
item[@"versionUrls"] = urls;
item[@"versionHashes"] = hashes;
item[@"versionDetailsLoaded"] = @(YES);
}
- (void)downloader:(MinecraftResourceDownloadTask *)downloader submitDownloadTasksFromPackage:(NSString *)packagePath toPath:(NSString *)destPath {
NSError *error;
UZKArchive *archive = [[UZKArchive alloc] initWithPath:packagePath error:&error];
if (error) {
[downloader finishDownloadWithErrorString:[NSString stringWithFormat:@"Failed to open modpack package: %@", error.localizedDescription]];
return;
}
NSData *indexData = [archive extractDataFromFile:@"modrinth.index.json" error:&error];
NSDictionary* indexDict = [NSJSONSerialization JSONObjectWithData:indexData options:kNilOptions error:&error];
if (error) {
[downloader finishDownloadWithErrorString:[NSString stringWithFormat:@"Failed to parse modrinth.index.json: %@", error.localizedDescription]];
return;
}
downloader.progress.totalUnitCount = [indexDict[@"files"] count];
for (NSDictionary *indexFile in indexDict[@"files"]) {
/*
if ([indexFile[@"downloads"] count] > 1) {
[downloader finishDownloadWithErrorString:[NSString stringWithFormat:@"Unhandled multiple files download %@", indexFile[@"downloads"]]];
return;
}
*/
NSString *url = [indexFile[@"downloads"] firstObject];
NSString *sha = indexFile[@"hashes"][@"sha1"];
NSString *path = [destPath stringByAppendingPathComponent:indexFile[@"path"]];
NSUInteger size = [indexFile[@"fileSize"] unsignedLongLongValue];
NSURLSessionDownloadTask *task = [downloader createDownloadTask:url size:size sha:sha altName:nil toPath:path];
if (task) {
[downloader.fileList addObject:indexFile[@"path"]];
[task resume];
} else if (!downloader.progress.cancelled) {
downloader.progress.completedUnitCount++;
} else {
return; // cancelled
}
}
[ModpackUtils archive:archive extractDirectory:@"overrides" toPath:destPath error:&error];
if (error) {
[downloader finishDownloadWithErrorString:[NSString stringWithFormat:@"Failed to extract overrides from modpack package: %@", error.localizedDescription]];
return;
}
[ModpackUtils archive:archive extractDirectory:@"client-overrides" toPath:destPath error:&error];
if (error) {
[downloader finishDownloadWithErrorString:[NSString stringWithFormat:@"Failed to extract client-overrides from modpack package: %@", error.localizedDescription]];
return;
}
// Delete package cache
[NSFileManager.defaultManager removeItemAtPath:packagePath error:nil];
// Download dependency client json (if available)
NSDictionary<NSString *, NSString *> *depInfo = [ModpackUtils infoForDependencies:indexDict[@"dependencies"]];
if (depInfo[@"json"]) {
NSString *jsonPath = [NSString stringWithFormat:@"%1$s/versions/%2$@/%[email protected]", getenv("POJAV_GAME_DIR"), depInfo[@"id"]];
NSURLSessionDownloadTask *task = [downloader createDownloadTask:depInfo[@"json"] size:0 sha:nil altName:nil toPath:jsonPath];
[task resume];
}
// TODO: automation for Forge
// Create profile
NSString *tmpIconPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"icon.png"];
PLProfiles.current.profiles[indexDict[@"name"]] = @{
@"gameDir": [NSString stringWithFormat:@"./custom_gamedir/%@", destPath.lastPathComponent],
@"name": indexDict[@"name"],
@"lastVersionId": depInfo[@"id"],
@"icon": [NSString stringWithFormat:@"data:image/png;base64,%@",
[[NSData dataWithContentsOfFile:tmpIconPath]
base64EncodedStringWithOptions:0]]
}.mutableCopy;
PLProfiles.current.selectedProfileName = indexDict[@"name"];
}
@end