All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.minecraftforge.gradle.common.tasks.DownloadAssets Maven / Gradle / Ivy

Go to download

Minecraft mod development framework used by Forge and FML for the gradle build system adapted for mohist api.

The newest version!
/*
 * ForgeGradle
 * Copyright (C) 2018 Forge Development LLC
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
 * USA
 */

package net.minecraftforge.gradle.common.tasks;

import net.minecraftforge.gradle.common.util.HashFunction;
import net.minecraftforge.gradle.common.util.Utils;
import net.minecraftforge.gradle.common.util.VersionJson;

import org.apache.commons.io.FileUtils;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.TaskAction;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public abstract class DownloadAssets extends DefaultTask {
    public DownloadAssets() {
        getAssetRepository().convention("https://resources.download.minecraft.net/");
        getConcurrentDownloads().convention(8);
    }

    @TaskAction
    public void run() throws IOException, InterruptedException {
        AssetIndex index = Utils.loadJson(getIndex(), AssetIndex.class);
        List keys = new ArrayList<>(index.objects.keySet());
        Collections.sort(keys);
        removeDuplicateRemotePaths(keys, index);

        File assetsPath = new File(Utils.getMCDir(), "/assets/objects");
        ExecutorService executorService = Executors.newFixedThreadPool(getConcurrentDownloads().get());
        CopyOnWriteArrayList failedDownloads = new CopyOnWriteArrayList<>();
        String assetRepo = getAssetRepository().get();
        for (String key : keys) {
            Asset asset = index.objects.get(key);
            File target = Utils.getCache(getProject(), "assets", "objects", asset.getPath());
            if (!target.exists() || !HashFunction.SHA1.hash(target).equals(asset.hash)) {
                URL url = new URL(assetRepo + asset.getPath());
                Runnable copyURLtoFile = () -> {
                    try {
                        File localFile = FileUtils.getFile(assetsPath + File.separator + asset.getPath());
                        if (localFile.exists()) {
                            getProject().getLogger().lifecycle("Copying local object: " + asset.getPath() + " Asset: " + key);
                            FileUtils.copyFile(localFile, target);
                        } else {
                            getProject().getLogger().lifecycle("Downloading: " + url + " Asset: " + key);
                            FileUtils.copyURLToFile(url, target, 10_000, 5_000);
                        }
                        if (!HashFunction.SHA1.hash(target).equals(asset.hash)) {
                            failedDownloads.add(key);
                            Utils.delete(target);
                            getProject().getLogger().error("{} Hash failed.", key);
                        }
                    } catch (IOException e) {
                        failedDownloads.add(key);
                        getProject().getLogger().error("{} Failed.", key);
                        e.printStackTrace();
                    }
                };
                executorService.execute(copyURLtoFile);
            }
        }
        executorService.shutdown();
        executorService.awaitTermination(8, TimeUnit.HOURS);
        if (!failedDownloads.isEmpty()) {
            String errorMessage = "";
            for (String key : failedDownloads) {
                errorMessage += "Failed to get asset: " + key + "\n";
            }
            errorMessage += "Some assets failed to download or validate, try running the task again.";
            throw new RuntimeException(errorMessage);
        }
    }

    // Some keys may reference the same remote file. Remove these duplicates to prevent two threads
    // writing to the same file on disk.
    private static void removeDuplicateRemotePaths(List keys, AssetIndex index) {
        Set seen = new HashSet<>(keys.size());
        keys.removeIf(key -> !seen.add(index.objects.get(key).getPath()));
    }

    private File getIndex() throws IOException {
        VersionJson json = Utils.loadJson(getMeta().get().getAsFile(), VersionJson.class);
        File target = Utils.getCache(getProject(), "assets", "indexes", json.assetIndex.id + ".json");
        return Utils.updateDownload(getProject(), target, json.assetIndex);
    }

    @InputFile
    public abstract RegularFileProperty getMeta();

    /**
     * The Base URL that will be used to download Minecraft assets.
     * A trailing slash is required.
     */
    @Internal
    public abstract Property getAssetRepository();

    /**
     * Defines how many threads will be used to download assets concurrently.
     */
    @Internal
    public abstract Property getConcurrentDownloads();

    @OutputDirectory
    public File getOutput() {
        return Utils.getCache(getProject(), "assets");
    }

    private static class AssetIndex {
        Map objects;
    }

    private static class Asset {
        String hash;

        public String getPath() {
            return hash.substring(0, 2) + '/' + hash;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy