Rewrite minecraft game data handling.
Much faster asset and native validation logic. Simplfied game metadata model.
This commit is contained in:
		
							parent
							
								
									24c166fc79
								
							
						
					
					
						commit
						0d1f40aee4
					
				
					 10 changed files with 442 additions and 343 deletions
				
			
		|  | @ -59,7 +59,7 @@ public class SetupIntelijRunConfigs { | |||
| 		if (extension.ideSync()) { | ||||
| 			//Ensures the assets are downloaded when idea is syncing a project | ||||
| 			MinecraftAssetsProvider.provide(extension.getMinecraftProvider(), project); | ||||
| 			MinecraftNativesProvider.provide(extension.getMinecraftProvider(), project); | ||||
| 			MinecraftNativesProvider.provide(project); | ||||
| 		} | ||||
| 
 | ||||
| 		String projectPath = project == rootProject ? "" : project.getPath().replace(':', '_'); | ||||
|  |  | |||
|  | @ -64,7 +64,7 @@ public class LaunchProvider extends DependencyProvider { | |||
| 				.property("client", "org.lwjgl.librarypath", getExtension().getNativesDirectory().getAbsolutePath()) | ||||
| 
 | ||||
| 				.argument("client", "--assetIndex") | ||||
| 				.argument("client", getExtension().getMinecraftProvider().getVersionInfo().assetIndex.getFabricId(getExtension().getMinecraftProvider().getMinecraftVersion())) | ||||
| 				.argument("client", getExtension().getMinecraftProvider().getVersionInfo().getAssetIndex().getFabricId(getExtension().getMinecraftProvider().getMinecraftVersion())) | ||||
| 				.argument("client", "--assetsDir") | ||||
| 				.argument("client", new File(getExtension().getUserCache(), "assets").getAbsolutePath()); | ||||
| 
 | ||||
|  |  | |||
|  | @ -43,7 +43,7 @@ import net.fabricmc.loom.LoomGradlePlugin; | |||
| import net.fabricmc.loom.configuration.DependencyProvider; | ||||
| import net.fabricmc.loom.configuration.providers.minecraft.ManifestVersion; | ||||
| import net.fabricmc.loom.configuration.providers.minecraft.MinecraftLibraryProvider; | ||||
| import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionInfo; | ||||
| import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; | ||||
| import net.fabricmc.loom.util.Constants; | ||||
| import net.fabricmc.loom.util.DownloadUtil; | ||||
| import net.fabricmc.loom.util.HashedDownloadUtil; | ||||
|  | @ -52,7 +52,7 @@ import net.fabricmc.stitch.merge.JarMerger; | |||
| public class MinecraftProvider extends DependencyProvider { | ||||
| 	private String minecraftVersion; | ||||
| 
 | ||||
| 	private MinecraftVersionInfo versionInfo; | ||||
| 	private MinecraftVersionMeta versionInfo; | ||||
| 	private MinecraftLibraryProvider libraryProvider; | ||||
| 
 | ||||
| 	private File minecraftJson; | ||||
|  | @ -76,7 +76,7 @@ public class MinecraftProvider extends DependencyProvider { | |||
| 		downloadMcJson(offline); | ||||
| 
 | ||||
| 		try (FileReader reader = new FileReader(minecraftJson)) { | ||||
| 			versionInfo = LoomGradlePlugin.GSON.fromJson(reader, MinecraftVersionInfo.class); | ||||
| 			versionInfo = LoomGradlePlugin.GSON.fromJson(reader, MinecraftVersionMeta.class); | ||||
| 		} | ||||
| 
 | ||||
| 		// Add Loom as an annotation processor | ||||
|  | @ -209,11 +209,11 @@ public class MinecraftProvider extends DependencyProvider { | |||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		MinecraftVersionInfo.Downloads client = versionInfo.downloads.get("client"); | ||||
| 		MinecraftVersionInfo.Downloads server = versionInfo.downloads.get("server"); | ||||
| 		MinecraftVersionMeta.Download client = versionInfo.getDownload("client"); | ||||
| 		MinecraftVersionMeta.Download server = versionInfo.getDownload("server"); | ||||
| 
 | ||||
| 		HashedDownloadUtil.downloadIfInvalid(new URL(client.url), minecraftClientJar, client.sha1, logger, false); | ||||
| 		HashedDownloadUtil.downloadIfInvalid(new URL(server.url), minecraftServerJar, server.sha1, logger, false); | ||||
| 		HashedDownloadUtil.downloadIfInvalid(new URL(client.getUrl()), minecraftClientJar, client.getSha1(), logger, false); | ||||
| 		HashedDownloadUtil.downloadIfInvalid(new URL(server.getUrl()), minecraftServerJar, server.getSha1(), logger, false); | ||||
| 	} | ||||
| 
 | ||||
| 	private void mergeJars(Logger logger) throws IOException { | ||||
|  | @ -233,7 +233,7 @@ public class MinecraftProvider extends DependencyProvider { | |||
| 		return minecraftVersion; | ||||
| 	} | ||||
| 
 | ||||
| 	public MinecraftVersionInfo getVersionInfo() { | ||||
| 	public MinecraftVersionMeta getVersionInfo() { | ||||
| 		return versionInfo; | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -55,7 +55,7 @@ import org.zeroturnaround.zip.ZipUtil; | |||
| 
 | ||||
| import net.fabricmc.loom.LoomGradleExtension; | ||||
| import net.fabricmc.loom.LoomGradlePlugin; | ||||
| import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionInfo; | ||||
| import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; | ||||
| import net.fabricmc.loom.util.DownloadUtil; | ||||
| import net.fabricmc.lorenztiny.TinyMappingsReader; | ||||
| import net.fabricmc.mapping.tree.TinyMappingFactory; | ||||
|  | @ -121,14 +121,14 @@ public class MojangMappingsDependency implements SelfResolvingDependency { | |||
| 	} | ||||
| 
 | ||||
| 	private MappingSet getMappingsSet(Path clientMappings, Path serverMappings) throws IOException { | ||||
| 		MinecraftVersionInfo versionInfo = extension.getMinecraftProvider().getVersionInfo(); | ||||
| 		MinecraftVersionMeta versionInfo = extension.getMinecraftProvider().getVersionInfo(); | ||||
| 
 | ||||
| 		if (versionInfo.downloads.get(MANIFEST_CLIENT_MAPPINGS) == null) { | ||||
| 		if (versionInfo.getDownload(MANIFEST_CLIENT_MAPPINGS) == null) { | ||||
| 			throw new RuntimeException("Failed to find official mojang mappings for " + getVersion()); | ||||
| 		} | ||||
| 
 | ||||
| 		String clientMappingsUrl = versionInfo.downloads.get(MANIFEST_CLIENT_MAPPINGS).url; | ||||
| 		String serverMappingsUrl = versionInfo.downloads.get(MANIFEST_SERVER_MAPPINGS).url; | ||||
| 		String clientMappingsUrl = versionInfo.getDownload(MANIFEST_CLIENT_MAPPINGS).getUrl(); | ||||
| 		String serverMappingsUrl = versionInfo.getDownload(MANIFEST_CLIENT_MAPPINGS).getUrl(); | ||||
| 
 | ||||
| 		DownloadUtil.downloadIfChanged(new URL(clientMappingsUrl), clientMappings.toFile(), project.getLogger()); | ||||
| 		DownloadUtil.downloadIfChanged(new URL(serverMappingsUrl), serverMappings.toFile(), project.getLogger()); | ||||
|  |  | |||
|  | @ -36,22 +36,13 @@ public class MinecraftLibraryProvider { | |||
| 	public File MINECRAFT_LIBS; | ||||
| 
 | ||||
| 	public void provide(MinecraftProvider minecraftProvider, Project project) { | ||||
| 		MinecraftVersionInfo versionInfo = minecraftProvider.getVersionInfo(); | ||||
| 		MinecraftVersionMeta versionInfo = minecraftProvider.getVersionInfo(); | ||||
| 
 | ||||
| 		initFiles(project, minecraftProvider); | ||||
| 
 | ||||
| 		for (MinecraftVersionInfo.Library library : versionInfo.libraries) { | ||||
| 			if (library.allowed() && !library.isNative() && library.getFile(MINECRAFT_LIBS) != null) { | ||||
| 				// TODO: Add custom library locations | ||||
| 
 | ||||
| 				// By default, they are all available on all sides | ||||
| 				/* boolean isClientOnly = false; | ||||
| 
 | ||||
| 				if (library.name.contains("java3d") || library.name.contains("paulscode") || library.name.contains("lwjgl") || library.name.contains("twitch") || library.name.contains("jinput") || library.name.contains("text2speech") || library.name.contains("objc")) { | ||||
| 					isClientOnly = true; | ||||
| 				} */ | ||||
| 
 | ||||
| 				project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, project.getDependencies().module(library.getArtifactName())); | ||||
| 		for (MinecraftVersionMeta.Library library : versionInfo.getLibraries()) { | ||||
| 			if (library.isValidForOS() && !library.hasNatives() && library.getArtifact() != null) { | ||||
| 				project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, project.getDependencies().module(library.getName())); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -27,24 +27,39 @@ package net.fabricmc.loom.configuration.providers.minecraft; | |||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.net.URL; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import org.apache.commons.io.FileUtils; | ||||
| import org.gradle.api.GradleException; | ||||
| import org.gradle.api.Project; | ||||
| import org.zeroturnaround.zip.ZipUtil; | ||||
| 
 | ||||
| import net.fabricmc.loom.LoomGradleExtension; | ||||
| import net.fabricmc.loom.configuration.providers.MinecraftProvider; | ||||
| import net.fabricmc.loom.util.DownloadUtil; | ||||
| import net.fabricmc.loom.LoomGradlePlugin; | ||||
| import net.fabricmc.loom.util.HashedDownloadUtil; | ||||
| 
 | ||||
| public class MinecraftNativesProvider { | ||||
| 	public static void provide(MinecraftProvider minecraftProvider, Project project) throws IOException { | ||||
| 		LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); | ||||
| 		MinecraftVersionInfo versionInfo = minecraftProvider.getVersionInfo(); | ||||
| 		boolean offline = project.getGradle().getStartParameter().isOffline(); | ||||
| 	private final Project project; | ||||
| 	private final LoomGradleExtension extension; | ||||
| 	private final File nativesDir; | ||||
| 	private final File jarStore; | ||||
| 
 | ||||
| 		File nativesDir = extension.getNativesDirectory(); | ||||
| 		File jarStore = extension.getNativesJarStore(); | ||||
| 	public MinecraftNativesProvider(Project project) { | ||||
| 		this.project = project; | ||||
| 		extension = project.getExtensions().getByType(LoomGradleExtension.class); | ||||
| 
 | ||||
| 		nativesDir = extension.getNativesDirectory(); | ||||
| 		jarStore = extension.getNativesJarStore(); | ||||
| 	} | ||||
| 
 | ||||
| 	public static void provide(Project project) throws IOException { | ||||
| 		new MinecraftNativesProvider(project).provide(); | ||||
| 	} | ||||
| 
 | ||||
| 	private void provide() throws IOException { | ||||
| 		if (extension.hasCustomNatives()) { | ||||
| 			if (!nativesDir.exists()) { | ||||
| 				throw new RuntimeException("Could no find custom natives directory at " + nativesDir.getAbsolutePath()); | ||||
|  | @ -53,21 +68,82 @@ public class MinecraftNativesProvider { | |||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		for (MinecraftVersionInfo.Library library : versionInfo.libraries) { | ||||
| 			File libJarFile = library.getFile(jarStore); | ||||
| 		if (!LoomGradlePlugin.refreshDeps && !requiresExtract()) { | ||||
| 			project.getLogger().info("Natives do no need extracting, skipping"); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		extractNatives(); | ||||
| 	} | ||||
| 
 | ||||
| 	private void extractNatives() throws IOException { | ||||
| 		boolean offline = project.getGradle().getStartParameter().isOffline(); | ||||
| 
 | ||||
| 		if (nativesDir.exists()) { | ||||
| 			try { | ||||
| 				FileUtils.deleteDirectory(nativesDir); | ||||
| 			} catch (IOException e) { | ||||
| 				throw new IOException("Failed to delete the natives directory, is the game running?", e); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		nativesDir.mkdirs(); | ||||
| 
 | ||||
| 		for (MinecraftVersionMeta.Classifier library : getNatives()) { | ||||
| 			File libJarFile = library.getRelativeFile(jarStore); | ||||
| 
 | ||||
| 			if (library.allowed() && library.isNative() && libJarFile != null) { | ||||
| 			if (!offline) { | ||||
| 					DownloadUtil.downloadIfChanged(new URL(library.getURL()), libJarFile, project.getLogger()); | ||||
| 				HashedDownloadUtil.downloadIfInvalid(new URL(library.getUrl()), libJarFile, library.getSha1(), project.getLogger(), false); | ||||
| 			} | ||||
| 
 | ||||
| 			if (!libJarFile.exists()) { | ||||
| 				throw new GradleException("Native jar not found at " + libJarFile.getAbsolutePath()); | ||||
| 			} | ||||
| 
 | ||||
| 				// TODO possibly find a way to prevent needing to re-extract after each run, doesnt seem too slow | ||||
| 			ZipUtil.unpack(libJarFile, nativesDir); | ||||
| 
 | ||||
| 			// Store a file containing the hash of the extracted natives, used on subsequent runs to skip extracting all the natives if they haven't changed | ||||
| 			File libSha1File = new File(nativesDir, libJarFile.getName() + ".sha1"); | ||||
| 			FileUtils.writeStringToFile(libSha1File, library.getSha1(), StandardCharsets.UTF_8); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private boolean requiresExtract() { | ||||
| 		List<MinecraftVersionMeta.Classifier> natives = getNatives(); | ||||
| 
 | ||||
| 		if (natives.isEmpty()) { | ||||
| 			throw new IllegalStateException("No natives found for the current system"); | ||||
| 		} | ||||
| 
 | ||||
| 		for (MinecraftVersionMeta.Classifier library : natives) { | ||||
| 			File libJarFile = library.getRelativeFile(jarStore); | ||||
| 			File libSha1File = new File(nativesDir, libJarFile.getName() + ".sha1"); | ||||
| 
 | ||||
| 			if (!libSha1File.exists()) { | ||||
| 				return true; | ||||
| 			} | ||||
| 
 | ||||
| 			try { | ||||
| 				String sha1 = FileUtils.readFileToString(libSha1File, StandardCharsets.UTF_8); | ||||
| 
 | ||||
| 				if (!sha1.equalsIgnoreCase(library.getSha1())) { | ||||
| 					return true; | ||||
| 				} | ||||
| 			} catch (IOException e) { | ||||
| 				project.getLogger().error("Failed to read " + libSha1File.getAbsolutePath(), e); | ||||
| 				return true; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// All looks good, no need to re-extract | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	private List<MinecraftVersionMeta.Classifier> getNatives() { | ||||
| 		return extension.getMinecraftProvider().getVersionInfo().getLibraries().stream() | ||||
| 				.filter((MinecraftVersionMeta.Library::hasNativesForOS)) | ||||
| 				.map(MinecraftVersionMeta.Library::getClassifierForOS) | ||||
| 				.filter(Objects::nonNull) | ||||
| 				.collect(Collectors.toList()); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -1,198 +0,0 @@ | |||
| /* | ||||
|  * This file is part of fabric-loom, licensed under the MIT License (MIT). | ||||
|  * | ||||
|  * Copyright (c) 2016, 2017, 2018 FabricMC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package net.fabricmc.loom.configuration.providers.minecraft; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import com.google.gson.JsonElement; | ||||
| import com.google.gson.JsonObject; | ||||
| 
 | ||||
| import net.fabricmc.loom.util.Constants; | ||||
| import net.fabricmc.loom.util.OperatingSystem; | ||||
| 
 | ||||
| public class MinecraftVersionInfo { | ||||
| 	public List<Library> libraries; | ||||
| 	public Map<String, Downloads> downloads; | ||||
| 	public AssetIndex assetIndex; | ||||
| 
 | ||||
| 	public class Downloads { | ||||
| 		public String url; | ||||
| 		public String sha1; | ||||
| 	} | ||||
| 
 | ||||
| 	public class AssetIndex { | ||||
| 		private String id; | ||||
| 		public String sha1; | ||||
| 		public String url; | ||||
| 
 | ||||
| 		public String getId() { | ||||
| 			return id; | ||||
| 		} | ||||
| 
 | ||||
| 		public String getFabricId(String version) { | ||||
| 			return id.equals(version) ? version : version + "-" + id; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public class Library { | ||||
| 		public String name; | ||||
| 		public JsonObject natives; | ||||
| 		public JsonObject downloads; | ||||
| 		private Artifact artifact; | ||||
| 		public Rule[] rules; | ||||
| 
 | ||||
| 		public String getURL() { | ||||
| 			String path; | ||||
| 			String[] parts = this.name.split(":", 3); | ||||
| 			path = parts[0].replace(".", "/") + "/" + parts[1] + "/" + parts[2] + "/" + parts[1] + "-" + parts[2] + getClassifier() + ".jar"; | ||||
| 			return Constants.LIBRARIES_BASE + path; | ||||
| 		} | ||||
| 
 | ||||
| 		public File getFile(File baseDir) { | ||||
| 			String[] parts = this.name.split(":", 3); | ||||
| 			return new File(baseDir, parts[0].replace(".", File.separator) + File.separator + parts[1] + File.separator + parts[2] + File.separator + parts[1] + "-" + parts[2] + getClassifier() + ".jar"); | ||||
| 		} | ||||
| 
 | ||||
| 		public String getSha1() { | ||||
| 			if (this.downloads == null) { | ||||
| 				return ""; | ||||
| 			} else if (this.downloads.getAsJsonObject("artifact") == null) { | ||||
| 				return ""; | ||||
| 			} else if (this.downloads.getAsJsonObject("artifact").get("sha1") == null) { | ||||
| 				return ""; | ||||
| 			} else { | ||||
| 				return this.downloads.getAsJsonObject("artifact").get("sha1").getAsString(); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public String getClassifier() { | ||||
| 			if (natives == null) { | ||||
| 				return ""; | ||||
| 			} else { | ||||
| 				JsonElement element = natives.get(OperatingSystem.getOS().replace("${arch}", OperatingSystem.getArch())); | ||||
| 
 | ||||
| 				if (element == null) { | ||||
| 					return ""; | ||||
| 				} | ||||
| 
 | ||||
| 				return "-" + element.getAsString().replace("\"", "").replace("${arch}", OperatingSystem.getArch()); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public boolean isNative() { | ||||
| 			return getClassifier().contains("natives"); | ||||
| 		} | ||||
| 
 | ||||
| 		public boolean allowed() { | ||||
| 			if (this.rules == null || this.rules.length <= 0) { | ||||
| 				return true; | ||||
| 			} | ||||
| 
 | ||||
| 			boolean success = false; | ||||
| 
 | ||||
| 			for (Rule rule : this.rules) { | ||||
| 				if (rule.os != null && rule.os.name != null) { | ||||
| 					if (rule.os.name.equalsIgnoreCase(OperatingSystem.getOS())) { | ||||
| 						return rule.action.equalsIgnoreCase("allow"); | ||||
| 					} | ||||
| 				} else { | ||||
| 					success = rule.action.equalsIgnoreCase("allow"); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			return success; | ||||
| 		} | ||||
| 
 | ||||
| 		public String getArtifactName() { | ||||
| 			if (artifact == null) { | ||||
| 				artifact = new Artifact(name); | ||||
| 			} | ||||
| 
 | ||||
| 			if (natives != null) { | ||||
| 				JsonElement jsonElement = natives.get(OperatingSystem.getOS()); | ||||
| 
 | ||||
| 				if (jsonElement != null) { | ||||
| 					return artifact.getArtifact(jsonElement.getAsString()); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			return artifact.getArtifact(artifact.classifier); | ||||
| 		} | ||||
| 
 | ||||
| 		private class Artifact { | ||||
| 			private final String domain, name, version, classifier, ext; | ||||
| 
 | ||||
| 			Artifact(String name) { | ||||
| 				String[] splitedArtifact = name.split(":"); | ||||
| 				int idx = splitedArtifact[splitedArtifact.length - 1].indexOf('@'); | ||||
| 
 | ||||
| 				if (idx != -1) { | ||||
| 					ext = splitedArtifact[splitedArtifact.length - 1].substring(idx + 1); | ||||
| 					splitedArtifact[splitedArtifact.length - 1] = splitedArtifact[splitedArtifact.length - 1].substring(0, idx); | ||||
| 				} else { | ||||
| 					ext = "jar"; | ||||
| 				} | ||||
| 
 | ||||
| 				this.domain = splitedArtifact[0]; | ||||
| 				this.name = splitedArtifact[1]; | ||||
| 				this.version = splitedArtifact[2]; | ||||
| 				this.classifier = splitedArtifact.length > 3 ? splitedArtifact[3] : null; | ||||
| 			} | ||||
| 
 | ||||
| 			public String getArtifact(String classifier) { | ||||
| 				String ret = domain + ":" + name + ":" + version; | ||||
| 
 | ||||
| 				if (classifier != null && classifier.indexOf('$') > -1) { | ||||
| 					classifier = classifier.replace("${arch}", Constants.SYSTEM_ARCH); | ||||
| 				} | ||||
| 
 | ||||
| 				if (classifier != null) { | ||||
| 					ret += ":" + classifier; | ||||
| 				} | ||||
| 
 | ||||
| 				if (!"jar".equals(ext)) { | ||||
| 					ret += "@" + ext; | ||||
| 				} | ||||
| 
 | ||||
| 				return ret; | ||||
| 			} | ||||
| 
 | ||||
| 			public String getClassifier() { | ||||
| 				return classifier; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private class Rule { | ||||
| 		public String action; | ||||
| 		public OS os; | ||||
| 
 | ||||
| 		private class OS { | ||||
| 			String name; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,277 @@ | |||
| /* | ||||
|  * This file is part of fabric-loom, licensed under the MIT License (MIT). | ||||
|  * | ||||
|  * Copyright (c) 2016, 2017, 2018 FabricMC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package net.fabricmc.loom.configuration.providers.minecraft; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Objects; | ||||
| 
 | ||||
| import com.google.gson.JsonObject; | ||||
| 
 | ||||
| import net.fabricmc.loom.util.OperatingSystem; | ||||
| 
 | ||||
| @SuppressWarnings("unused") | ||||
| public final class MinecraftVersionMeta { | ||||
| 	private JsonObject arguments; | ||||
| 	private AssetIndex assetIndex; | ||||
| 	private String assets; | ||||
| 	private int complianceLevel; | ||||
| 	private Map<String, Download> downloads; | ||||
| 	private String id; | ||||
| 	private List<Library> libraries; | ||||
| 	private JsonObject logging; | ||||
| 	private String mainClass; | ||||
| 	private int minimumLauncherVersion; | ||||
| 	private String releaseTime; | ||||
| 	private String time; | ||||
| 	private String type; | ||||
| 
 | ||||
| 	public Download getDownload(String key) { | ||||
| 		return getDownloads().get(key); | ||||
| 	} | ||||
| 
 | ||||
| 	public JsonObject getArguments() { | ||||
| 		return arguments; | ||||
| 	} | ||||
| 
 | ||||
| 	public AssetIndex getAssetIndex() { | ||||
| 		return assetIndex; | ||||
| 	} | ||||
| 
 | ||||
| 	public String getAssets() { | ||||
| 		return assets; | ||||
| 	} | ||||
| 
 | ||||
| 	public int getComplianceLevel() { | ||||
| 		return complianceLevel; | ||||
| 	} | ||||
| 
 | ||||
| 	public Map<String, Download> getDownloads() { | ||||
| 		return downloads; | ||||
| 	} | ||||
| 
 | ||||
| 	public String getId() { | ||||
| 		return id; | ||||
| 	} | ||||
| 
 | ||||
| 	public List<Library> getLibraries() { | ||||
| 		return libraries; | ||||
| 	} | ||||
| 
 | ||||
| 	public JsonObject getLogging() { | ||||
| 		return logging; | ||||
| 	} | ||||
| 
 | ||||
| 	public String getMainClass() { | ||||
| 		return mainClass; | ||||
| 	} | ||||
| 
 | ||||
| 	public int getMinimumLauncherVersion() { | ||||
| 		return minimumLauncherVersion; | ||||
| 	} | ||||
| 
 | ||||
| 	public String getReleaseTime() { | ||||
| 		return releaseTime; | ||||
| 	} | ||||
| 
 | ||||
| 	public String getTime() { | ||||
| 		return time; | ||||
| 	} | ||||
| 
 | ||||
| 	public String getType() { | ||||
| 		return type; | ||||
| 	} | ||||
| 
 | ||||
| 	public final class AssetIndex extends Downloadable { | ||||
| 		private String id; | ||||
| 		private long totalSize; | ||||
| 
 | ||||
| 		public String getFabricId(String version) { | ||||
| 			return id.equals(version) ? version : version + "-" + id; | ||||
| 		} | ||||
| 
 | ||||
| 		public String getId() { | ||||
| 			return id; | ||||
| 		} | ||||
| 
 | ||||
| 		public long getTotalSize() { | ||||
| 			return totalSize; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public final class Download extends Downloadable { | ||||
| 	} | ||||
| 
 | ||||
| 	public final class Library { | ||||
| 		private Downloads downloads; | ||||
| 		private String name; | ||||
| 		private Map<String, String> natives; | ||||
| 		private List<Rule> rules; | ||||
| 
 | ||||
| 		public boolean isValidForOS() { | ||||
| 			if (rules == null || rules.isEmpty()) { | ||||
| 				return true; | ||||
| 			} | ||||
| 
 | ||||
| 			for (Rule rule : rules) { | ||||
| 				if (rule.appliesToOS() && !rule.isAllowed()) { | ||||
| 					return false; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			return true; | ||||
| 		} | ||||
| 
 | ||||
| 		public boolean hasNatives() { | ||||
| 			return this.natives != null; | ||||
| 		} | ||||
| 
 | ||||
| 		public boolean hasNativesForOS() { | ||||
| 			if (!hasNatives()) { | ||||
| 				return false; | ||||
| 			} | ||||
| 
 | ||||
| 			if (natives.get(OperatingSystem.getOS()) == null) { | ||||
| 				return false; | ||||
| 			} | ||||
| 
 | ||||
| 			return isValidForOS(); | ||||
| 		} | ||||
| 
 | ||||
| 		public Classifier getClassifierForOS() { | ||||
| 			return getDownloads().getClassifier(natives.get(OperatingSystem.getOS())); | ||||
| 		} | ||||
| 
 | ||||
| 		public Downloads getDownloads() { | ||||
| 			return downloads; | ||||
| 		} | ||||
| 
 | ||||
| 		public Artifact getArtifact() { | ||||
| 			if (getDownloads() == null) { | ||||
| 				return null; | ||||
| 			} | ||||
| 
 | ||||
| 			return getDownloads().getArtifact(); | ||||
| 		} | ||||
| 
 | ||||
| 		public String getName() { | ||||
| 			return name; | ||||
| 		} | ||||
| 
 | ||||
| 		public Map<String, String> getNatives() { | ||||
| 			return natives; | ||||
| 		} | ||||
| 
 | ||||
| 		public List<Rule> getRules() { | ||||
| 			return rules; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public final class Downloads { | ||||
| 		private Artifact artifact; | ||||
| 		private Map<String, Classifier> classifiers; | ||||
| 
 | ||||
| 		public Classifier getClassifier(String os) { | ||||
| 			return classifiers.get(os); | ||||
| 		} | ||||
| 
 | ||||
| 		public Artifact getArtifact() { | ||||
| 			return artifact; | ||||
| 		} | ||||
| 
 | ||||
| 		public Map<String, Classifier> getClassifiers() { | ||||
| 			return classifiers; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public final class Artifact extends Downloadable { | ||||
| 	} | ||||
| 
 | ||||
| 	public final class Classifier extends Downloadable { | ||||
| 	} | ||||
| 
 | ||||
| 	public final class Rule { | ||||
| 		private String action; | ||||
| 		private OS os; | ||||
| 
 | ||||
| 		public boolean appliesToOS() { | ||||
| 			return getOS() == null || getOS().isValidForOS(); | ||||
| 		} | ||||
| 
 | ||||
| 		public boolean isAllowed() { | ||||
| 			return getAction().equals("allow"); | ||||
| 		} | ||||
| 
 | ||||
| 		public String getAction() { | ||||
| 			return action; | ||||
| 		} | ||||
| 
 | ||||
| 		public OS getOS() { | ||||
| 			return os; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public final class OS { | ||||
| 		private String name; | ||||
| 
 | ||||
| 		public boolean isValidForOS() { | ||||
| 			return getName() == null || getName().equalsIgnoreCase(OperatingSystem.getOS()); | ||||
| 		} | ||||
| 
 | ||||
| 		public String getName() { | ||||
| 			return name; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// A base class for everything that can be downloaded | ||||
| 	public abstract class Downloadable { | ||||
| 		private String path; | ||||
| 		private String sha1; | ||||
| 		private long size; | ||||
| 		private String url; | ||||
| 
 | ||||
| 		public File getRelativeFile(File baseDirectory) { | ||||
| 			Objects.requireNonNull(getPath(), "Cannot get relative file from a null path"); | ||||
| 			return new File(baseDirectory, getPath()); | ||||
| 		} | ||||
| 
 | ||||
| 		public String getPath() { | ||||
| 			return path; | ||||
| 		} | ||||
| 
 | ||||
| 		public String getSha1() { | ||||
| 			return sha1; | ||||
| 		} | ||||
| 
 | ||||
| 		public long getSize() { | ||||
| 			return size; | ||||
| 		} | ||||
| 
 | ||||
| 		public String getUrl() { | ||||
| 			return url; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -26,32 +26,25 @@ package net.fabricmc.loom.configuration.providers.minecraft.assets; | |||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.FileReader; | ||||
| import java.io.FileWriter; | ||||
| import java.io.IOException; | ||||
| import java.net.URL; | ||||
| import java.util.Deque; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.ConcurrentHashMap; | ||||
| import java.util.concurrent.ConcurrentLinkedDeque; | ||||
| import java.util.concurrent.ExecutorService; | ||||
| import java.util.concurrent.Executors; | ||||
| import java.util.concurrent.TimeUnit; | ||||
| 
 | ||||
| import com.google.common.base.Stopwatch; | ||||
| import com.google.common.hash.Hashing; | ||||
| import com.google.common.io.Files; | ||||
| import com.google.gson.Gson; | ||||
| import com.google.gson.reflect.TypeToken; | ||||
| import org.gradle.api.GradleException; | ||||
| import org.gradle.api.Project; | ||||
| 
 | ||||
| import net.fabricmc.loom.LoomGradleExtension; | ||||
| import net.fabricmc.loom.LoomGradlePlugin; | ||||
| import net.fabricmc.loom.configuration.providers.MinecraftProvider; | ||||
| import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionInfo; | ||||
| import net.fabricmc.loom.util.Checksum; | ||||
| import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; | ||||
| import net.fabricmc.loom.util.Constants; | ||||
| import net.fabricmc.loom.util.DownloadUtil; | ||||
| import net.fabricmc.loom.util.HashedDownloadUtil; | ||||
| import net.fabricmc.loom.util.gradle.ProgressLogger; | ||||
| 
 | ||||
| public class MinecraftAssetsProvider { | ||||
|  | @ -59,8 +52,8 @@ public class MinecraftAssetsProvider { | |||
| 		LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); | ||||
| 		boolean offline = project.getGradle().getStartParameter().isOffline(); | ||||
| 
 | ||||
| 		MinecraftVersionInfo versionInfo = minecraftProvider.getVersionInfo(); | ||||
| 		MinecraftVersionInfo.AssetIndex assetIndex = versionInfo.assetIndex; | ||||
| 		MinecraftVersionMeta versionInfo = minecraftProvider.getVersionInfo(); | ||||
| 		MinecraftVersionMeta.AssetIndex assetIndex = versionInfo.getAssetIndex(); | ||||
| 
 | ||||
| 		// get existing cache files | ||||
| 		File assets = new File(extension.getUserCache(), "assets"); | ||||
|  | @ -70,9 +63,7 @@ public class MinecraftAssetsProvider { | |||
| 		} | ||||
| 
 | ||||
| 		File assetsInfo = new File(assets, "indexes" + File.separator + assetIndex.getFabricId(minecraftProvider.getMinecraftVersion()) + ".json"); | ||||
| 		File checksumInfo = new File(assets, "checksum" + File.separator + minecraftProvider.getMinecraftVersion() + ".json"); | ||||
| 
 | ||||
| 		if (!assetsInfo.exists() || !Checksum.equals(assetsInfo, assetIndex.sha1)) { | ||||
| 		project.getLogger().info(":downloading asset index"); | ||||
| 
 | ||||
| 		if (offline) { | ||||
|  | @ -84,28 +75,16 @@ public class MinecraftAssetsProvider { | |||
| 				throw new GradleException("Asset index not found at " + assetsInfo.getAbsolutePath()); | ||||
| 			} | ||||
| 		} else { | ||||
| 				DownloadUtil.downloadIfChanged(new URL(assetIndex.url), assetsInfo, project.getLogger()); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		Gson gson = new Gson(); | ||||
| 		Map<String, String> checksumInfos = new ConcurrentHashMap<>(); | ||||
| 
 | ||||
| 		if (checksumInfo.exists()) { | ||||
| 			try (FileReader reader = new FileReader(checksumInfo)) { | ||||
| 				checksumInfos.putAll(gson.fromJson(reader, new TypeToken<Map<String, String>>() { | ||||
| 				}.getType())); | ||||
| 			} | ||||
| 			HashedDownloadUtil.downloadIfInvalid(new URL(assetIndex.getUrl()), assetsInfo, assetIndex.getSha1(), project.getLogger(), false); | ||||
| 		} | ||||
| 
 | ||||
| 		Deque<ProgressLogger> loggers = new ConcurrentLinkedDeque<>(); | ||||
| 		ExecutorService executor = Executors.newFixedThreadPool(Math.min(10, Math.max(Runtime.getRuntime().availableProcessors() / 2, 1))); | ||||
| 		int toDownload = 0; | ||||
| 
 | ||||
| 		AssetIndex index; | ||||
| 
 | ||||
| 		try (FileReader fileReader = new FileReader(assetsInfo)) { | ||||
| 			index = gson.fromJson(fileReader, AssetIndex.class); | ||||
| 			index = LoomGradlePlugin.GSON.fromJson(fileReader, AssetIndex.class); | ||||
| 		} | ||||
| 
 | ||||
| 		Stopwatch stopwatch = Stopwatch.createStarted(); | ||||
|  | @ -118,16 +97,6 @@ public class MinecraftAssetsProvider { | |||
| 			String filename = "objects" + File.separator + sha1.substring(0, 2) + File.separator + sha1; | ||||
| 			File file = new File(assets, filename); | ||||
| 
 | ||||
| 			String localFileChecksum = !file.exists() ? null : checksumInfos.computeIfAbsent(entry.getKey(), path -> { | ||||
| 				try { | ||||
| 					return Files.asByteSource(file).hash(Hashing.sha1()).toString(); | ||||
| 				} catch (IOException e) { | ||||
| 					e.printStackTrace(); | ||||
| 					return null; | ||||
| 				} | ||||
| 			}); | ||||
| 
 | ||||
| 			if (LoomGradlePlugin.refreshDeps || localFileChecksum == null || !localFileChecksum.equals(sha1)) { | ||||
| 			if (offline) { | ||||
| 				if (file.exists()) { | ||||
| 					project.getLogger().warn("Outdated asset " + entry.getKey()); | ||||
|  | @ -135,7 +104,6 @@ public class MinecraftAssetsProvider { | |||
| 					throw new GradleException("Asset " + entry.getKey() + " not found at " + file.getAbsolutePath()); | ||||
| 				} | ||||
| 			} else { | ||||
| 					toDownload++; | ||||
| 				executor.execute(() -> { | ||||
| 					ProgressLogger progressLogger; | ||||
| 
 | ||||
|  | @ -159,34 +127,19 @@ public class MinecraftAssetsProvider { | |||
| 					progressLogger.progress(String.format("%-30.30s", assetName) + " - " + sha1); | ||||
| 
 | ||||
| 					try { | ||||
| 							DownloadUtil.downloadIfChanged(new URL(Constants.RESOURCES_BASE + sha1.substring(0, 2) + "/" + sha1), file, project.getLogger(), true); | ||||
| 						HashedDownloadUtil.downloadIfInvalid(new URL(Constants.RESOURCES_BASE + sha1.substring(0, 2) + "/" + sha1), file, sha1, project.getLogger(), true); | ||||
| 					} catch (IOException e) { | ||||
| 						throw new RuntimeException("Failed to download: " + assetName, e); | ||||
| 					} | ||||
| 
 | ||||
| 						if (localFileChecksum == null) { | ||||
| 							checksumInfos.put(entry.getKey(), sha1); | ||||
| 						} | ||||
| 
 | ||||
| 					//Give this logger back | ||||
| 					loggers.add(progressLogger); | ||||
| 				}); | ||||
| 			} | ||||
| 		} | ||||
| 		} | ||||
| 
 | ||||
| 		project.getLogger().info("Took " + stopwatch.stop() + " to iterate " + parent.size() + " asset index."); | ||||
| 
 | ||||
| 		if (toDownload > 0) { | ||||
| 			project.getLogger().lifecycle(":downloading " + toDownload + " asset" + (toDownload == 1 ? "" : "s") + "..."); | ||||
| 		} | ||||
| 
 | ||||
| 		checksumInfo.getParentFile().mkdirs(); | ||||
| 
 | ||||
| 		try (FileWriter writer = new FileWriter(checksumInfo)) { | ||||
| 			gson.toJson(checksumInfos, writer); | ||||
| 		} | ||||
| 
 | ||||
| 		//Wait for the assets to all download | ||||
| 		executor.shutdown(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -40,6 +40,6 @@ public class DownloadAssetsTask extends AbstractLoomTask { | |||
| 		LoomGradleExtension extension = getExtension(); | ||||
| 
 | ||||
| 		MinecraftAssetsProvider.provide(extension.getMinecraftProvider(), project); | ||||
| 		MinecraftNativesProvider.provide(extension.getMinecraftProvider(), project); | ||||
| 		MinecraftNativesProvider.provide(project); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue