195 lines
8.9 KiB
C++
195 lines
8.9 KiB
C++
#include "fakemojang.h"
|
|
|
|
#include "fixtures.h"
|
|
|
|
#include "minecraftversion.h"
|
|
|
|
#include <QJsonArray>
|
|
#include <QJsonDocument>
|
|
|
|
namespace {
|
|
|
|
const QString kMeta = QStringLiteral("https://piston-meta.mojang.com/v1/packages/");
|
|
const QString kData = QStringLiteral("https://piston-data.mojang.com/v1/objects/");
|
|
const QString kLibraries = QStringLiteral("https://libraries.minecraft.net/");
|
|
const QString kResources = QStringLiteral("https://resources.download.minecraft.net/");
|
|
|
|
QJsonObject download(const QString &url, const QByteArray &data, const QString &path = {})
|
|
{
|
|
QJsonObject object{{"url", url}, {"sha1", Fixtures::sha1(data)}, {"size", data.size()}};
|
|
if (!path.isEmpty())
|
|
object.insert("path", path);
|
|
return object;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
FakeMojang::FakeMojang(FakeInternet &net)
|
|
: m_net(net)
|
|
{
|
|
}
|
|
|
|
QString FakeMojang::manifestUrl()
|
|
{
|
|
return QStringLiteral("https://launchermeta.mojang.com/mc/game/version_manifest_v2.json");
|
|
}
|
|
|
|
QString FakeMojang::addObject(const QString &relativePath, const QByteArray &data)
|
|
{
|
|
QString url;
|
|
if (relativePath.startsWith(QLatin1String("libraries/")))
|
|
url = kLibraries + relativePath.mid(int(sizeof("libraries/") - 1));
|
|
else if (relativePath.startsWith(QLatin1String("assets/objects/")))
|
|
url = kResources + relativePath.mid(int(sizeof("assets/objects/") - 1));
|
|
else
|
|
url = kData + Fixtures::sha1(data) + u'/' + relativePath.section(u'/', -1);
|
|
|
|
m_files.insert(relativePath, {url, data});
|
|
m_net.route(url, FakeResponse::bytes(data));
|
|
return url;
|
|
}
|
|
|
|
void FakeMojang::overrideFile(const QString &relativePath, const FakeResponse &response)
|
|
{
|
|
m_net.route(m_files.value(relativePath).url, response);
|
|
}
|
|
|
|
QString FakeMojang::fileUrl(const QString &relativePath) const
|
|
{
|
|
return m_files.value(relativePath).url;
|
|
}
|
|
|
|
void FakeMojang::publish(const VersionSpec &spec)
|
|
{
|
|
QHash<QString, QByteArray> expected;
|
|
QJsonObject json{{"id", spec.id}, {"type", spec.type}, {"releaseTime", spec.releaseTime}};
|
|
|
|
if (!spec.inheritsFrom.isEmpty()) {
|
|
json.insert("inheritsFrom", spec.inheritsFrom);
|
|
json.insert("mainClass", "net.fabricmc.loader.impl.launch.knot.KnotClient");
|
|
const QString libPath = QStringLiteral("net/fabricmc/fabric-loader/0.16.10/fabric-loader-0.16.10.jar");
|
|
const QByteArray libData = "fabric-loader-" + spec.id.toUtf8();
|
|
const QString url = addObject("libraries/" + libPath, libData);
|
|
json.insert("libraries", QJsonArray{QJsonObject{
|
|
{"name", "net.fabricmc:fabric-loader:0.16.10"},
|
|
{"downloads", QJsonObject{{"artifact", download(url, libData, libPath)}}}}});
|
|
expected.insert("libraries/" + libPath, libData);
|
|
} else {
|
|
json.insert("mainClass", "net.minecraft.client.main.Main");
|
|
json.insert("arguments", QJsonObject{
|
|
{"game", QJsonArray{"--username", "${auth_player_name}", "--version", "${version_name}",
|
|
"--gameDir", "${game_directory}", "--assetsDir", "${assets_root}",
|
|
"--assetIndex", "${assets_index_name}", "--uuid", "${auth_uuid}",
|
|
"--accessToken", "${auth_access_token}", "--userType", "${user_type}",
|
|
"--versionType", "${version_type}"}},
|
|
{"jvm", QJsonArray{"-Djava.library.path=${natives_directory}", "-cp", "${classpath}"}}});
|
|
json.insert("javaVersion", QJsonObject{{"component", "java-runtime-gamma"}, {"majorVersion", spec.javaMajor}});
|
|
|
|
// client.jar
|
|
const QString clientPath = "versions/" + spec.id + u'/' + spec.id + ".jar";
|
|
const QByteArray clientData = "client-jar-of-" + spec.id.toUtf8();
|
|
const QString clientUrl = addObject(clientPath, clientData);
|
|
json.insert("downloads", QJsonObject{{"client", download(clientUrl, clientData)}});
|
|
expected.insert(clientPath, clientData);
|
|
|
|
// Обычная библиотека и натив под текущую платформу.
|
|
const QString native = VersionLoader::nativeClassifier();
|
|
const QString libPath = QStringLiteral("com/mojang/brigadier/1.1.8/brigadier-1.1.8.jar");
|
|
const QByteArray libData = "brigadier";
|
|
const QString nativePath = QStringLiteral("org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-%1.jar").arg(native);
|
|
const QByteArray nativeData = Fixtures::zipBytes({{"liblwjgl.so", "so"}, {"lwjgl.dll", "dll"},
|
|
{"liblwjgl.dylib", "dylib"}});
|
|
const QString libUrl = addObject("libraries/" + libPath, libData);
|
|
const QString nativeUrl = addObject("libraries/" + nativePath, nativeData);
|
|
json.insert("libraries", QJsonArray{
|
|
QJsonObject{{"name", "com.mojang:brigadier:1.1.8"},
|
|
{"downloads", QJsonObject{{"artifact", download(libUrl, libData, libPath)}}}},
|
|
QJsonObject{{"name", "org.lwjgl:lwjgl:3.3.1:" + native},
|
|
{"downloads", QJsonObject{{"artifact", download(nativeUrl, nativeData, nativePath)}}},
|
|
{"rules", QJsonArray{QJsonObject{{"action", "allow"},
|
|
{"os", QJsonObject{{"name", VersionLoader::osName()}}}}}}}});
|
|
expected.insert("libraries/" + libPath, libData);
|
|
expected.insert("libraries/" + nativePath, nativeData);
|
|
|
|
// Ассеты.
|
|
const QString indexId = spec.legacyAssets ? QStringLiteral("legacy") : spec.id + "-assets";
|
|
QJsonObject objects;
|
|
qint64 total = 0;
|
|
for (const QString &name : {QStringLiteral("minecraft/sounds/click.ogg"), QStringLiteral("icons/icon_16x16.png")}) {
|
|
const QByteArray data = (name + "@" + spec.id).toUtf8();
|
|
const QString hash = Fixtures::sha1(data);
|
|
const QString objectPath = "assets/objects/" + hash.left(2) + u'/' + hash;
|
|
addObject(objectPath, data);
|
|
objects.insert(name, QJsonObject{{"hash", hash}, {"size", data.size()}});
|
|
expected.insert(objectPath, data);
|
|
if (spec.legacyAssets)
|
|
expected.insert("assets/virtual/legacy/" + name, data);
|
|
total += data.size();
|
|
}
|
|
QJsonObject index{{"objects", objects}};
|
|
if (spec.legacyAssets)
|
|
index.insert("virtual", true);
|
|
const QByteArray indexData = QJsonDocument(index).toJson(QJsonDocument::Compact);
|
|
const QString indexPath = "assets/indexes/" + indexId + ".json";
|
|
const QString indexUrl = addObject(indexPath, indexData);
|
|
QJsonObject assetIndex = download(indexUrl, indexData);
|
|
assetIndex.insert("id", indexId);
|
|
assetIndex.insert("totalSize", total);
|
|
json.insert("assetIndex", assetIndex);
|
|
json.insert("assets", indexId);
|
|
expected.insert(indexPath, indexData);
|
|
|
|
// Конфиг логирования.
|
|
const QByteArray logData = "<Configuration/>";
|
|
const QString logPath = QStringLiteral("assets/log_configs/client-1.12.xml");
|
|
const QString logUrl = addObject(logPath, logData);
|
|
QJsonObject logFile = download(logUrl, logData);
|
|
logFile.insert("id", "client-1.12.xml");
|
|
json.insert("logging", QJsonObject{{"client", QJsonObject{
|
|
{"argument", "-Dlog4j.configurationFile=${path}"},
|
|
{"file", logFile}, {"type", "log4j2-xml"}}}});
|
|
expected.insert(logPath, logData);
|
|
}
|
|
|
|
const QByteArray jsonData = QJsonDocument(json).toJson(QJsonDocument::Compact);
|
|
m_versionJson.insert(spec.id, jsonData);
|
|
expected.insert("versions/" + spec.id + u'/' + spec.id + ".json", jsonData);
|
|
m_expected.insert(spec.id, expected);
|
|
m_net.route(versionJsonUrl(spec.id), FakeResponse::json(jsonData));
|
|
|
|
m_specs.removeIf([&](const VersionSpec &s) { return s.id == spec.id; });
|
|
m_specs << spec;
|
|
routeManifest();
|
|
}
|
|
|
|
QString FakeMojang::versionJsonUrl(const QString &id) const
|
|
{
|
|
return kMeta + Fixtures::sha1(m_versionJson.value(id)) + u'/' + id + ".json";
|
|
}
|
|
|
|
QByteArray FakeMojang::versionJson(const QString &id) const
|
|
{
|
|
return m_versionJson.value(id);
|
|
}
|
|
|
|
void FakeMojang::routeManifest()
|
|
{
|
|
QJsonArray versions;
|
|
for (const VersionSpec &spec : std::as_const(m_specs)) {
|
|
if (!spec.listed)
|
|
continue;
|
|
versions << QJsonObject{{"id", spec.id},
|
|
{"type", spec.type},
|
|
{"url", versionJsonUrl(spec.id)},
|
|
{"sha1", Fixtures::sha1(m_versionJson.value(spec.id))},
|
|
{"releaseTime", spec.releaseTime},
|
|
{"time", spec.releaseTime}};
|
|
}
|
|
m_net.route(manifestUrl(), FakeResponse::json(QJsonObject{{"versions", versions}}));
|
|
}
|
|
|
|
QHash<QString, QByteArray> FakeMojang::expectedFiles(const QString &id) const
|
|
{
|
|
return m_expected.value(id);
|
|
}
|