Skip to content

Add Legacy Fabric Metadata #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions GenerateLegacyFabric.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import json
import os

from meta.common import ensure_component_dir, polymc_path, upstream_path, transform_maven_key
from meta.common.fabric import JARS_DIR, INSTALLER_INFO_DIR, META_DIR, INTERMEDIARY_COMPONENT, LOADER_COMPONENT
from meta.model import MetaVersion, Dependency, Library, MetaPackage, GradleSpecifier
from meta.model.fabric import FabricJarInfo, FabricInstallerDataV1, FabricMainClasses

PMC_DIR = polymc_path()
UPSTREAM_DIR = upstream_path()

ensure_component_dir(LOADER_COMPONENT)
ensure_component_dir(INTERMEDIARY_COMPONENT)


def load_jar_info(artifact_key) -> FabricJarInfo:
return FabricJarInfo.parse_file(os.path.join(UPSTREAM_DIR, JARS_DIR, f"{artifact_key}.json"))


def load_installer_info(version) -> FabricInstallerDataV1:
return FabricInstallerDataV1.parse_file(os.path.join(UPSTREAM_DIR, INSTALLER_INFO_DIR, f"{version}.json"))


def process_loader_version(entry) -> MetaVersion:
jar_info = load_jar_info(transform_maven_key(entry["maven"]))
installer_info = load_installer_info(entry["version"])

v = MetaVersion(name="Legacy Fabric Loader", uid="net.fabricmc.fabric-loader", version=entry["version"])
v.release_time = jar_info.release_time
v.requires = [Dependency(uid='net.fabricmc.intermediary')]
v.order = 10
v.type = "release"
if isinstance(installer_info.main_class, FabricMainClasses):
v.main_class = installer_info.main_class.client
else:
v.main_class = installer_info.main_class
v.libraries = []
v.libraries.extend(installer_info.libraries.common)
v.libraries.extend(installer_info.libraries.client)
loader_lib = Library(name=GradleSpecifier.from_string(entry["maven"]), url="https://maven.legacyfabric.net")
v.libraries.append(loader_lib)
return v


def process_intermediary_version(entry) -> MetaVersion:
jar_info = load_jar_info(transform_maven_key(entry["maven"]))

v = MetaVersion(name="Legacy Fabric Intermediary Mappings", uid="net.fabricmc.intermediary", version=entry["version"])
v.release_time = jar_info.release_time
v.requires = [Dependency(uid='net.minecraft', equals=entry["version"])]
v.order = 11
v.type = "release"
v.libraries = []
v.volatile = True
intermediary_lib = Library(name=GradleSpecifier.from_string(entry["maven"]), url="https://maven.legacyfabric.net")
v.libraries.append(intermediary_lib)
return v


def main():
recommended_loader_versions = []
recommended_intermediary_versions = []

with open(os.path.join(UPSTREAM_DIR, META_DIR, "loader.json"), 'r', encoding='utf-8') as f:
loader_version_index = json.load(f)
for entry in loader_version_index:
version = entry["version"]
print(f"Processing loader {version}")

v = process_loader_version(entry)

if not recommended_loader_versions: # first (newest) loader is recommended
recommended_loader_versions.append(version)

v.write(os.path.join(PMC_DIR, LOADER_COMPONENT, f"{v.version}.json"))

with open(os.path.join(UPSTREAM_DIR, META_DIR, "intermediary.json"), 'r', encoding='utf-8') as f:
intermediary_version_index = json.load(f)
for entry in intermediary_version_index:
version = entry["version"]
print(f"Processing intermediary {version}")

v = process_intermediary_version(entry)

recommended_intermediary_versions.append(version) # all intermediaries are recommended

v.write(os.path.join(PMC_DIR, INTERMEDIARY_COMPONENT, f"{v.version}.json"))

package = MetaPackage(uid=LOADER_COMPONENT, name='Legacy Fabric Loader')
package.recommended = recommended_loader_versions
package.description = "Legacy Fabric is a project based on the Fabric Project. Its two main priorities are to keep parity with upstream and to support as many legacy versions as possible."
package.project_url = "https://legacyfabric.net"
package.authors = ["Legacy Fabric Developers"]
package.write(os.path.join(PMC_DIR, LOADER_COMPONENT, "package.json"))

package = MetaPackage(uid=INTERMEDIARY_COMPONENT, name='Legacy Fabric Intermediary Mappings')
package.recommended = recommended_intermediary_versions
package.description = "Intermediary mappings allow using Legacy Fabric Loader with mods for Minecraft in a more compatible manner."
package.project_url = "https://legacyfabric.net"
package.authors = ["Legacy Fabric Developers"]
package.write(os.path.join(PMC_DIR, INTERMEDIARY_COMPONENT, "package.json"))


if __name__ == '__main__':
main()
2 changes: 2 additions & 0 deletions update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ upstream_git checkout "${BRANCH}" || exit 1
python updateMojang.py || fail_in
python updateForge.py || fail_in
python updateFabric.py || fail_in
python updateLegacyFabric.py || fail_in
python updateQuilt.py || fail_in
python updateLiteloader.py || fail_in

Expand All @@ -65,6 +66,7 @@ polymc_git checkout "${BRANCH}" || exit 1
python generateMojang.py || fail_out
python generateForge.py || fail_out
python generateFabric.py || fail_out
python generateLegacyFabric.py || fail_out
python generateQuilt.py || fail_out
python generateLiteloader.py || fail_out
python index.py || fail_out
Expand Down
121 changes: 121 additions & 0 deletions updateLegacyFabric.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import hashlib
import json
import os
import zipfile
from datetime import datetime

import requests
from cachecontrol import CacheControl
from cachecontrol.caches import FileCache

from meta.common import upstream_path, ensure_upstream_dir, transform_maven_key
from meta.common.fabric import JARS_DIR, INSTALLER_INFO_DIR, META_DIR, DATETIME_FORMAT_HTTP
from meta.model.fabric import FabricJarInfo

UPSTREAM_DIR = upstream_path()

ensure_upstream_dir(JARS_DIR)
ensure_upstream_dir(INSTALLER_INFO_DIR)
ensure_upstream_dir(META_DIR)

forever_cache = FileCache('caches/http_cache', forever=True)
sess = CacheControl(requests.Session(), forever_cache)


def filehash(filename, hashtype, blocksize=65536):
h = hashtype()
with open(filename, "rb") as f:
for block in iter(lambda: f.read(blocksize), b""):
h.update(block)
return h.hexdigest()


def get_maven_url(maven_key, server, ext):
parts = maven_key.split(":", 3)
maven_ver_url = server + parts[0].replace(".", "/") + "/" + parts[1] + "/" + parts[2] + "/"
maven_url = maven_ver_url + parts[1] + "-" + parts[2] + ext
return maven_url


def get_json_file(path, url):
with open(path, 'w', encoding='utf-8') as f:
r = sess.get(url)
r.raise_for_status()
version_json = r.json()
json.dump(version_json, f, sort_keys=True, indent=4)
return version_json


def get_plaintext(url):
r = sess.get(url)
r.raise_for_status()
return r.text


def head_file(url):
r = sess.head(url)
r.raise_for_status()
return r.headers


def get_binary_file(path, url):
with open(path, 'wb') as f:
r = sess.get(url)
r.raise_for_status()
for chunk in r.iter_content(chunk_size=128):
f.write(chunk)


def compute_jar_file(path, url):
# These two approaches should result in the same metadata, except for the timestamp which might be a few minutes
# off for the fallback method
try:
# Let's not download a Jar file if we don't need to.
headers = head_file(url)
tstamp = datetime.strptime(headers["Last-Modified"], DATETIME_FORMAT_HTTP)
sha1 = get_plaintext(url + ".sha1")
sha256 = get_plaintext(url + ".sha256")
size = int(headers["Content-Length"])
except requests.HTTPError:
# Some older versions don't have a .sha256 file :(
print(f"Falling back to downloading jar for {url}")

jar_path = path + ".jar"
get_binary_file(jar_path, url)
tstamp = datetime.fromtimestamp(0)
with zipfile.ZipFile(jar_path) as jar:
allinfo = jar.infolist()
for info in allinfo:
tstamp_new = datetime(*info.date_time)
if tstamp_new > tstamp:
tstamp = tstamp_new

sha1 = filehash(jar_path, hashlib.sha1)
sha256 = filehash(jar_path, hashlib.sha256)
size = os.path.getsize(jar_path)

data = FabricJarInfo(release_time=tstamp, sha1=sha1, sha256=sha256, size=size)
data.write(path + ".json")


def main():
# get the version list for each component we are interested in
for component in ["intermediary", "loader"]:
index = get_json_file(os.path.join(UPSTREAM_DIR, META_DIR, f"{component}.json"),
"https://meta.legacyfabric.net/v2/versions/" + component)
for it in index:
print(f"Processing {component} {it['version']} ")
jar_maven_url = get_maven_url(it["maven"], "https://maven.legacyfabric.net/", ".jar")
compute_jar_file(os.path.join(UPSTREAM_DIR, JARS_DIR, transform_maven_key(it["maven"])), jar_maven_url)

# for each loader, download installer JSON file from maven
with open(os.path.join(UPSTREAM_DIR, META_DIR, "loader.json"), 'r', encoding='utf-8') as loaderVersionIndexFile:
loader_version_index = json.load(loaderVersionIndexFile)
for it in loader_version_index:
print(f"Downloading installer info for loader {it['version']} ")
maven_url = get_maven_url(it["maven"], "https://maven.legacyfabric.net/", ".json")
get_json_file(os.path.join(UPSTREAM_DIR, INSTALLER_INFO_DIR, f"{it['version']}.json"), maven_url)


if __name__ == '__main__':
main()