205 lines
10 KiB
C#
205 lines
10 KiB
C#
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.IO;
|
||
|
using System.IO.Compression;
|
||
|
using System.Linq;
|
||
|
using System.Net;
|
||
|
using System.Reflection;
|
||
|
using System.Text;
|
||
|
using Newtonsoft.Json.Linq;
|
||
|
|
||
|
namespace McMan
|
||
|
{
|
||
|
internal class Program
|
||
|
{
|
||
|
public static void Main(string[] args)
|
||
|
{
|
||
|
if (args.Length == 0)
|
||
|
{
|
||
|
args = new string[1];
|
||
|
Console.WriteLine("Username");
|
||
|
Console.Write("> ");
|
||
|
args[0] = Console.ReadLine();
|
||
|
}
|
||
|
if (args.Length == 1)
|
||
|
{
|
||
|
args = new [] {args[0], "choose"};
|
||
|
}
|
||
|
if (args.Length == 2)
|
||
|
{
|
||
|
Console.WriteLine("Max memory (empty for 2G)");
|
||
|
Console.Write("> ");
|
||
|
args = new [] {args[0], args[1], Console.ReadLine()};
|
||
|
}
|
||
|
if (Directory.Exists("assets"))
|
||
|
Directory.Delete("assets", true);
|
||
|
if (Directory.Exists("Game"))
|
||
|
Directory.Delete("Game", true);
|
||
|
Directory.CreateDirectory("Game");
|
||
|
using WebClient client = new WebClient();
|
||
|
string maxMem = args[2];
|
||
|
maxMem = string.IsNullOrWhiteSpace(maxMem) ? "2G" : maxMem;
|
||
|
Environment.CurrentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||
|
JObject gameJson = GetMinecraftJson(client, args[1], out string ver);
|
||
|
DownloadClient(client, gameJson["downloads"]["client"]);
|
||
|
DownloadAssets(client, gameJson["assetIndex"], ver);
|
||
|
string classpath = DownloadLibs(client, (JArray) gameJson["libraries"]);
|
||
|
Console.WriteLine("Creating launch script");
|
||
|
classpath += "client.jar";
|
||
|
const string mainClass = "net.minecraft.client.main.Main";
|
||
|
const string java = "javaw";
|
||
|
const string lowMem = "768M";
|
||
|
string javaOptions =
|
||
|
$"-server -splash:splash.png -d64 -da -dsa -Xrs -Xms{lowMem} -Xmx{maxMem} -XX:NewSize={lowMem} -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:-UseAdaptiveSizePolicy -XX:+DisableExplicitGC -Djava.library.path=libraries -cp {classpath} {mainClass}";
|
||
|
string jvmBat =
|
||
|
$"start /D %cd% /I /HIGH {java} {javaOptions} --username {args[0]} --version {ver} --gameDir %cd% --assetsDir assets --assetIndex {gameJson.Value<string>("assets")} --uuid 2536abce90e8476a871679918164abc5 --accessToken 99abe417230342cb8e9e2168ab46297a --userType legacy --versionType release --nativeLauncherVersion 307";
|
||
|
File.WriteAllText(Path.Combine("Game", "start.bat"), jvmBat);
|
||
|
Console.WriteLine("Done!");
|
||
|
}
|
||
|
|
||
|
//Downloads a JRE, doesn't start
|
||
|
/*private static void GetJRE(WebClient client, bool x64)
|
||
|
{
|
||
|
Console.WriteLine("Fetching JRE Versions...");
|
||
|
IEnumerable<JToken> json = JArray.Parse(client.DownloadString("https://api.adoptopenjdk.net/v2/info/releases/openjdk11"))
|
||
|
.SelectMany(s => (JArray)s["binaries"]);
|
||
|
Console.WriteLine("Selecting version...");
|
||
|
string download = json.Last(s =>
|
||
|
s.Value<string>("os") == "windows" && s.Value<string>("architecture") == (x64 ? "x64" : "x32") &&
|
||
|
s.Value<string>("binary_type") == "jre" && s.Value<string>("heap_size") == "normal")
|
||
|
.Value<string>("binary_link");
|
||
|
Console.WriteLine($"Downloading from {download}...");
|
||
|
if (Directory.Exists("jdk"))
|
||
|
Directory.Delete("jdk", true);
|
||
|
Directory.CreateDirectory("jdk");
|
||
|
using MemoryStream ms = new MemoryStream(client.DownloadData(download));
|
||
|
Console.WriteLine("Extracting...");
|
||
|
using ZipArchive archive = new ZipArchive(ms);
|
||
|
archive.ExtractToDirectory("jdk");
|
||
|
Microsoft.VisualBasic.FileIO.FileSystem.CopyDirectory(Directory.GetDirectories("jdk")[0], "Game");
|
||
|
Directory.Delete("jdk", true);
|
||
|
}*/
|
||
|
|
||
|
private static string DownloadLibs(WebClient client, JArray libraries)
|
||
|
{
|
||
|
string output = "";
|
||
|
string libdir = Path.Combine("Game", "libraries");
|
||
|
Console.WriteLine("Downloading libs...");
|
||
|
Directory.CreateDirectory(libdir);
|
||
|
Console.CursorTop++;
|
||
|
JToken[] tmp = libraries.ToArray();
|
||
|
for (int i = 0; i < tmp.Length; i++)
|
||
|
{
|
||
|
JToken lib = tmp[i];
|
||
|
if (lib["downloads"].Any(s => ((JProperty) s).Name == "classifiers"))
|
||
|
output = lib["downloads"]["classifiers"].Where(lib2 => ((JProperty) lib2).Name == "natives-windows")
|
||
|
.Aggregate(
|
||
|
output,
|
||
|
(current, lib2) => current + DownloadLib(client, lib2.First.Value<string>("sha1"),
|
||
|
lib2.First.Value<string>("url"), lib2.First.Value<string>("path"),
|
||
|
libdir));
|
||
|
Console.CursorLeft = 0;
|
||
|
Console.CursorTop--;
|
||
|
Console.WriteLine($"[{i + 1}/{tmp.Length}] Getting {lib["downloads"]["artifact"].Value<string>("path")}{new string(' ', 10)}");
|
||
|
output += DownloadLib(client,
|
||
|
lib["downloads"]["artifact"].Value<string>("sha1"),
|
||
|
lib["downloads"]["artifact"].Value<string>("url"),
|
||
|
lib["downloads"]["artifact"].Value<string>("path"),
|
||
|
libdir);
|
||
|
}
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
private static string DownloadLib(WebClient client, string hash, string url, string path, string libdir)
|
||
|
{
|
||
|
string tmp = path.Split('/').Aggregate(libdir, Path.Combine);
|
||
|
byte[] libB = client.DownloadData(url);
|
||
|
if (Tools.Hash(libB) != hash)
|
||
|
{
|
||
|
Console.WriteLine("ERROR: HASH MISMATCH IN LIBRARY");
|
||
|
throw new Exception();
|
||
|
}
|
||
|
Directory.CreateDirectory(Path.GetDirectoryName(tmp));
|
||
|
File.WriteAllBytes(tmp, libB);
|
||
|
return $"{path.Split('/').Aggregate("libraries", Path.Combine)};";
|
||
|
}
|
||
|
|
||
|
private static void DownloadAssets(WebClient client, JToken assetIndex, string version)
|
||
|
{
|
||
|
bool success = false;
|
||
|
byte[] indexB = new byte[0];
|
||
|
while (!success)
|
||
|
{
|
||
|
Console.WriteLine("Downloading asset index...");
|
||
|
indexB = client.DownloadData(assetIndex.Value<string>("url"));
|
||
|
success = Tools.Hash(indexB) == assetIndex.Value<string>("sha1");
|
||
|
}
|
||
|
JObject index = JObject.Parse(Encoding.Default.GetString(indexB));
|
||
|
string assetPath = Path.Combine("Game", "assets");
|
||
|
Directory.CreateDirectory(Path.Combine(assetPath, "indexes"));
|
||
|
File.WriteAllBytes(Path.Combine(assetPath, "indexes", Path.GetFileName(assetIndex.Value<string>("url"))), indexB);
|
||
|
Console.WriteLine("Processing...");
|
||
|
Console.CursorTop++;
|
||
|
JToken[] tmp = Enumerable.ToArray(index["objects"]);
|
||
|
for (int i = 0; i < tmp.Length; i++)
|
||
|
{
|
||
|
JProperty asset = (JProperty)tmp[i];
|
||
|
string name = asset.Name;
|
||
|
Console.CursorLeft = 0;
|
||
|
Console.CursorTop--;
|
||
|
Console.WriteLine($"[{i + 1}/{tmp.Length}] Getting {name}{new string(' ', 10)}");
|
||
|
string hash = asset.First.Value<string>("hash");
|
||
|
string url = $"http://resources.download.minecraft.net/{string.Join("", hash.Substring(0, 2))}/{hash}";
|
||
|
byte[] file = client.DownloadData(url);
|
||
|
if (Tools.Hash(file) != hash)
|
||
|
{
|
||
|
Console.WriteLine("ERROR: HASH MISMATCH IN ASSET");
|
||
|
throw new Exception();
|
||
|
}
|
||
|
string path = Path.Combine(assetPath, "objects", hash.Substring(0, 2));
|
||
|
if (!Directory.Exists(path))
|
||
|
Directory.CreateDirectory(path);
|
||
|
File.WriteAllBytes(Path.Combine(path, hash), file);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static void DownloadClient(WebClient client, JToken clientJson)
|
||
|
{
|
||
|
bool success = false;
|
||
|
byte[] mcClient = new byte[0];
|
||
|
while (!success)
|
||
|
{
|
||
|
Console.WriteLine("Downloading client...");
|
||
|
mcClient = client.DownloadData(clientJson.Value<string>("url"));
|
||
|
success = Tools.Hash(mcClient) == clientJson.Value<string>("sha1");
|
||
|
}
|
||
|
File.WriteAllBytes(Path.Combine("Game", "client.jar"), mcClient);
|
||
|
}
|
||
|
|
||
|
private static JObject GetMinecraftJson(WebClient client, string verdat, out string version)
|
||
|
{
|
||
|
Tuple<string, string> tmp;
|
||
|
Tuple<string, string>[] versions = GetVersions(client);
|
||
|
if (verdat == "choose")
|
||
|
{
|
||
|
Console.WriteLine("Select a Version (empty for latest)");
|
||
|
for (int i = 0; i < versions.Length; i++) Console.WriteLine($"{i + 1}\t- {versions[i].Item1}");
|
||
|
Console.Write("> ");
|
||
|
string inp = Console.ReadLine();
|
||
|
tmp = string.IsNullOrWhiteSpace(inp) ? versions.Last() : versions[int.Parse(inp) - 1];
|
||
|
}
|
||
|
else
|
||
|
tmp = versions.First(s => s.Item1.ToLower().Equals(verdat.ToLower(), StringComparison.InvariantCultureIgnoreCase));
|
||
|
version = tmp.Item1;
|
||
|
Console.WriteLine($"Using minecraft {version}");
|
||
|
return JObject.Parse(client.DownloadString(tmp.Item2));
|
||
|
}
|
||
|
|
||
|
private static Tuple<string, string>[] GetVersions(WebClient client)
|
||
|
{
|
||
|
JObject tmp =
|
||
|
JObject.Parse(client.DownloadString("https://launchermeta.mojang.com/mc/game/version_manifest.json"));
|
||
|
return tmp["versions"].Where(token => token.Value<string>("type") == "release").Select(token => new Tuple<string, string>(token.Value<string>("id"), token.Value<string>("url"))).OrderBy(s => Version.Parse(s.Item1)).ToArray();
|
||
|
}
|
||
|
}
|
||
|
}
|