Inceptum/reference/mcman.cs

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();
}
}
}