Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* A Gradle plugin for the creation of Minecraft mods and MinecraftForge plugins.
* Copyright (C) 2013-2019 Minecraft Forge
* Copyright (C) 2020-2021 anatawa12 and other contributors
*
* 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.tasks;
import groovy.lang.Closure;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import net.minecraftforge.gradle.common.Constants;
import net.minecraftforge.gradle.util.delayed.DelayedFile;
import net.minecraftforge.gradle.util.json.JsonFactory;
import net.minecraftforge.gradle.util.json.version.AssetIndex;
import net.minecraftforge.gradle.util.json.version.AssetIndex.AssetEntry;
import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.TaskAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.io.Files;
public class DownloadAssetsTask extends DefaultTask
{
DelayedFile assetsDir;
Object assetIndex;
private File virtualRoot = null;
private final File minecraftDir = new File(Constants.getMinecraftDirectory(), "assets/objects");
private static final int MAX_TRIES = 5;
@TaskAction
public void doTask() throws IOException, InterruptedException
{
File outDir = new File(getAssetsDir(), "objects");
if (!outDir.exists() || !outDir.isDirectory())
{
outDir.mkdirs();
}
File indexFile = getAssetsIndex();
AssetIndex index = JsonFactory.loadAssetsIndex(indexFile);
// check virtual
if (index.virtual)
{
virtualRoot = new File(getAssetsDir(), "virtual/" + Files.getNameWithoutExtension(indexFile.getName()));
virtualRoot.mkdirs();
}
// make thread pool
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*2);
for (Entry e : index.objects.entrySet())
{
Asset asset = new Asset(e.getKey(), e.getValue().hash, e.getValue().size);
executor.submit(new GetAssetTask(asset, outDir, minecraftDir, virtualRoot));
}
executor.shutdown(); // complete all the tasks then shutdown.
int max = (int) executor.getTaskCount(); // its gonna be somewhere around 600-700 I think
// as long as the excutor isnt dead yet.
while (!executor.awaitTermination(1, TimeUnit.SECONDS))
{
int done = (int) executor.getCompletedTaskCount();
getLogger().lifecycle("Current status: {}/{} {}%", done, max, (int) ((double) done / max * 100));
}
}
public File getAssetsDir()
{
return assetsDir.call();
}
public void setAssetsDir(DelayedFile assetsDir)
{
this.assetsDir = assetsDir;
}
public File getAssetsIndex()
{
return getProject().file(assetIndex);
}
public void setAssetsIndex(Closure index)
{
this.assetIndex = index;
}
private static class Asset
{
public final String name;
public final String path;
public final String hash;
public final long size;
Asset(String name, String hash, long size)
{
this.name = name;
this.path = hash.substring(0, 2) + "/" + hash;
this.hash = hash.toLowerCase();
this.size = size;
}
}
private static boolean checkFileCorrupt(File file, long size, String expectedHash)
{
if (!file.exists())
return true;
if (file.length() != size)
return true;
if (!expectedHash.equalsIgnoreCase(Constants.hash(file, "SHA1")))
return true;
return false;
}
private static class GetAssetTask implements Callable
{
private static final Logger LOGGER = LoggerFactory.getLogger(GetAssetTask.class);
private final Asset asset;
private final File assetDir, minecraftDir, virtualRoot;
private GetAssetTask(Asset asset, File assetDir, File minecraftDir, File virtualRoot)
{
this.asset = asset;
this.assetDir = assetDir;
this.minecraftDir = minecraftDir;
this.virtualRoot = virtualRoot;
}
@Override
public Boolean call()
{
boolean worked = true;
for (int tryNum = 1; tryNum < MAX_TRIES + 1; tryNum++)
{
try
{
File file = new File(assetDir, asset.path);
if (checkFileCorrupt(file, asset.size, asset.hash))
{
file.delete();
}
// if it exists, its good, so we dont do this stuff...
if (!file.exists())
{
file.getParentFile().mkdirs();
File localMc = new File(minecraftDir, asset.path);
if (checkFileCorrupt(localMc, asset.size, asset.hash))
{
// download
ReadableByteChannel channel = Channels.newChannel(new URL(Constants.URL_ASSETS + "/" + asset.path).openStream());
try (FileOutputStream fout = new FileOutputStream(file);
FileChannel fileChannel = fout.getChannel())
{
fileChannel.transferFrom(channel, 0, asset.size);
}
}
else
{
// copy from MC
Constants.copyFile(localMc, file, asset.size);
}
}
if (virtualRoot != null)
{
File virtual = new File(virtualRoot, asset.name);
if (checkFileCorrupt(virtual, asset.size, asset.hash))
{
virtual.delete();
Constants.copyFile(file, virtual);
}
}
}
catch (Exception e)
{
LOGGER.error("Error downloading asset (try {}) : {}", tryNum, asset.name);
e.printStackTrace();
worked = false;
}
}
return worked;
}
}
}