From 2ca56cafdf767c32fd8397bc4089c1e8513272d5 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sat, 2 Apr 2022 19:35:02 +0200 Subject: refactor: move generateMojang to pydantic models --- meta/common/__init__.py | 5 +- meta/common/mojang.py | 6 +- meta/metautil.py | 169 ------------------------------------------------ meta/model/__init__.py | 42 ++++++++---- meta/model/mojang.py | 140 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 174 insertions(+), 188 deletions(-) (limited to 'meta') diff --git a/meta/common/__init__.py b/meta/common/__init__.py index 478404362a..ce773e4e18 100644 --- a/meta/common/__init__.py +++ b/meta/common/__init__.py @@ -5,7 +5,10 @@ DATETIME_FORMAT_HTTP = "%a, %d %b %Y %H:%M:%S %Z" def serialize_datetime(dt: datetime.datetime): - return dt.replace(tzinfo=datetime.timezone.utc).isoformat() + if dt.tzinfo is None: + dt.replace(tzinfo=datetime.timezone.utc).isoformat() + + return dt.isoformat() def polymc_path(): diff --git a/meta/common/mojang.py b/meta/common/mojang.py index 1c16afc885..3bf8281942 100644 --- a/meta/common/mojang.py +++ b/meta/common/mojang.py @@ -7,6 +7,8 @@ VERSIONS_DIR = join(BASE_DIR, "versions") ASSETS_DIR = join(BASE_DIR, "assets") STATIC_EXPERIMENTS_FILE = join(BASE_DIR, "minecraft-experiments.json") +STATIC_OVERRIDES_FILE = join(BASE_DIR, "minecraft-legacy-override.json") -MINECRAFT_COMPONENT = "" -LWJGL_COMPONENT = "" +MINECRAFT_COMPONENT = "net.minecraft" +LWJGL_COMPONENT = "org.lwjgl" +LWJGL3_COMPONENT = "org.lwjgl3" diff --git a/meta/metautil.py b/meta/metautil.py index 11c4009868..ee8994acb3 100644 --- a/meta/metautil.py +++ b/meta/metautil.py @@ -99,53 +99,6 @@ class GradleSpecifierProperty(JsonProperty): return value, value.toString() -''' -Mojang index files look like this: -{ - "latest": { - "release": "1.11.2", - "snapshot": "17w06a" - }, - "versions": [ - ... - { - "id": "17w06a", - "releaseTime": "2017-02-08T13:16:29+00:00", - "time": "2017-02-08T13:17:20+00:00", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/mc/game/7db0c61afa278d016cf1dae2fba0146edfbf2f8e/17w06a.json" - }, - ... - ] -} -''' - - -class MojangIndexEntry(JsonObject): - id = StringProperty() - releaseTime = ISOTimestampProperty() - time = ISOTimestampProperty() - type = StringProperty() - url = StringProperty() - sha1 = StringProperty(exclude_if_none=True, default=None) - complianceLevel = IntegerProperty(exclude_if_none=True, default=None) - - -class MojangIndex(JsonObject): - latest = DictProperty(StringProperty) - versions = ListProperty(MojangIndexEntry) - - -class MojangIndexWrap: - def __init__(self, json): - self.index = MojangIndex.wrap(json) - self.latest = self.index.latest - versionsDict = {} - for version in self.index.versions: - versionsDict[version.id] = version - self.versions = versionsDict - - class MojangArtifactBase(JsonObject): sha1 = StringProperty(exclude_if_none=True, default=None) size = IntegerProperty(exclude_if_none=True, default=None) @@ -303,75 +256,11 @@ class PolyMCVersionFile(VersionedJsonObject): minecraftArguments = StringProperty(exclude_if_none=True, default=None) releaseTime = ISOTimestampProperty(exclude_if_none=True, default=None) type = StringProperty(exclude_if_none=True, default=None) - compatibleJavaMajors = ListProperty(int, exclude_if_none=True, default=None) addTraits = ListProperty(StringProperty, name="+traits", exclude_if_none=True, default=None) addTweakers = ListProperty(StringProperty, name="+tweakers", exclude_if_none=True, default=None) order = IntegerProperty(exclude_if_none=True, default=None) -class UnknownComplianceLevelException(Exception): - """Exception raised for unknown Mojang compliance level - - Attributes: - message -- explanation of the error - """ - - def __init__(self, message): - self.message = message - - -# Convert Mojang version file object to a PolyMC version file object -def MojangToPolyMC(file, name, uid, version): - pmcFile = PolyMCVersionFile( - { - "name": name, - "uid": uid, - "version": version - } - ) - pmcFile.assetIndex = file.assetIndex - pmcFile.libraries = file.libraries - pmcFile.mainClass = file.mainClass - if file.id: - mainJar = PolyMCLibrary( - { - "name": "com.mojang:minecraft:%s:client" % file.id, - } - ) - cldl = file.downloads['client'] - mainJar.downloads = MojangLibraryDownloads() - mainJar.downloads.artifact = MojangArtifact() - mainJar.downloads.artifact.path = None - mainJar.downloads.artifact.url = cldl.url - mainJar.downloads.artifact.sha1 = cldl.sha1 - mainJar.downloads.artifact.size = cldl.size - pmcFile.mainJar = mainJar - - pmcFile.minecraftArguments = file.minecraftArguments - pmcFile.releaseTime = file.releaseTime - # time should not be set. - pmcFile.type = file.type - - if file.javaVersion is not None: # some versions don't have this. TODO: maybe maintain manual overrides - major = file.javaVersion.majorVersion - pmcFile.compatibleJavaMajors = [major] - if major == 16: # TODO: deal with this somewhere else - pmcFile.compatibleJavaMajors.append(17) - - maxSupportedLevel = 1 - if file.complianceLevel: - if file.complianceLevel == 0: - pass - elif file.complianceLevel == 1: - if not pmcFile.addTraits: - pmcFile.addTraits = [] - pmcFile.addTraits.append("XR:Initial") - else: - raise UnknownComplianceLevelException("Unsupported Mojang compliance level: %d. Max supported is: %d" % ( - file.complianceLevel, maxSupportedLevel)) - return pmcFile - - class PolyMCSharedPackageData(VersionedJsonObject): name = StringProperty(required=True) uid = StringProperty(required=True) @@ -388,15 +277,6 @@ class PolyMCSharedPackageData(VersionedJsonObject): print("Error while trying to save shared packaged data for %s:" % self.uid, e) -def writeSharedPackageData(uid, name): - desc = PolyMCSharedPackageData({ - 'name': name, - 'uid': uid - }) - with open(PMC_DIR + "/%s/package.json" % uid, 'w') as file: - json.dump(desc.to_json(), file, sort_keys=True, indent=4) - - def readSharedPackageData(uid): with open(PMC_DIR + "/%s/package.json" % uid, 'r') as file: return PolyMCSharedPackageData(json.load(file)) @@ -427,52 +307,3 @@ class PolyMCPackageIndexEntry(JsonObject): class PolyMCPackageIndex(VersionedJsonObject): packages = ListProperty(PolyMCPackageIndexEntry) - - -''' -The PolyMC static override file for legacy looks like this: -{ - "versions": [ - ... - { - "id": "c0.0.13a", - "checksum": "3617fbf5fbfd2b837ebf5ceb63584908", - "releaseTime": "2009-05-31T00:00:00+02:00", - "type": "old_alpha", - "mainClass": "com.mojang.minecraft.Minecraft", - "appletClass": "com.mojang.minecraft.MinecraftApplet", - "+traits": ["legacyLaunch", "no-texturepacks"] - }, - ... - ] -} -''' - - -class LegacyOverrideEntry(JsonObject): - releaseTime = ISOTimestampProperty(exclude_if_none=True, default=None) - mainClass = StringProperty(exclude_if_none=True, default=None) - appletClass = StringProperty(exclude_if_none=True, default=None) - addTraits = ListProperty(StringProperty, name="+traits", exclude_if_none=True, default=None) - - -class LegacyOverrideIndex(JsonObject): - versions = DictProperty(LegacyOverrideEntry) - - -def ApplyLegacyOverride(pmcFile, legacyOverride): - # simply hard override classes - pmcFile.mainClass = legacyOverride.mainClass - pmcFile.appletClass = legacyOverride.appletClass - # if we have an updated release time (more correct than Mojang), use it - if legacyOverride.releaseTime != None: - pmcFile.releaseTime = legacyOverride.releaseTime - # add traits, if any - if legacyOverride.addTraits: - if not pmcFile.addTraits: - pmcFile.addTraits = [] - pmcFile.addTraits = pmcFile.addTraits + legacyOverride.addTraits - # remove all libraries - they are not needed for legacy - pmcFile.libraries = None - # remove minecraft arguments - we use our own hardcoded ones - pmcFile.minecraftArguments = None diff --git a/meta/model/__init__.py b/meta/model/__init__.py index 3b343a4a02..73c1e80a42 100644 --- a/meta/model/__init__.py +++ b/meta/model/__init__.py @@ -1,4 +1,3 @@ -import os.path from datetime import datetime from typing import Optional, List, Dict, Any @@ -41,7 +40,8 @@ class MetaBase(pydantic.BaseModel): class Versioned(MetaBase): @validator("format_version") def format_version_must_be_supported(cls, v): - return v > META_FORMAT_VERSION + assert v > META_FORMAT_VERSION + return v format_version: int = Field(META_FORMAT_VERSION, alias="formatVersion") @@ -80,13 +80,14 @@ class MojangLibraryExtractRules(MetaBase): class MojangLibraryDownloads(MetaBase): artifact: Optional[MojangArtifact] - classifiers: Dict[Any, MojangArtifact] + classifiers: Optional[Dict[Any, MojangArtifact]] class OSRule(MetaBase): @validator("name") def name_must_be_os(cls, v): - return v in ["osx", "linux", "windows"] + assert v in ["osx", "linux", "windows"] + return v name: str version: Optional[str] @@ -95,21 +96,40 @@ class OSRule(MetaBase): class MojangRule(MetaBase): @validator("action") def action_must_be_allow_disallow(cls, v): - return v in ["allow", "disallow"] + assert v in ["allow", "disallow"] + return v action: str os: Optional[OSRule] +class MojangRules(MetaBase): + __root__: List[MojangRule] + + def __iter__(self): + return iter(self.__root__) + + def __getitem__(self, item): + return self.__root__[item] + + class MojangLibrary(MetaBase): + @validator("name") + def validate_name(cls, v): + if v is not GradleSpecifier: + return GradleSpecifier(v) + return v + extract: Optional[MojangLibraryExtractRules] name: GradleSpecifier downloads: Optional[MojangLibraryDownloads] natives: Optional[Dict[str, str]] - rules: Optional[List[MojangRule]] + rules: Optional[MojangRules] - class Config: - arbitrary_types_allowed = True + +class Library(MojangLibrary): + url: Optional[str] + mmcHint: Optional[AnyHttpUrl] = Field(None, alias="MMC-hint") class Dependency(MetaBase): @@ -118,11 +138,6 @@ class Dependency(MetaBase): suggests: Optional[str] -class Library(MojangLibrary): - url: Optional[str] - mmcHint: Optional[AnyHttpUrl] = Field(None, alias="MMC-hint") - - class MetaVersionFile(Versioned): name: str version: str @@ -141,6 +156,7 @@ class MetaVersionFile(Versioned): applet_class: Optional[str] = Field(alias="appletClass") minecraft_arguments: Optional[str] = Field(alias="minecraftArguments") release_time: Optional[datetime] = Field(alias="releaseTime") + compatible_java_majors: Optional[List[int]] = Field(alias="compatibleJavaMajors") additional_traits: Optional[List[str]] = Field(alias="+traits") additional_tweakers: Optional[List[str]] = Field(alias="+tweakers") diff --git a/meta/model/mojang.py b/meta/model/mojang.py index fa00071dd7..1d739f4725 100644 --- a/meta/model/mojang.py +++ b/meta/model/mojang.py @@ -1,9 +1,13 @@ from datetime import datetime -from typing import Optional, List, Dict +from typing import Optional, List, Dict, Any -from pydantic import AnyHttpUrl +from pydantic import AnyHttpUrl, validator, Field -from . import MetaBase +from . import MetaBase, MojangArtifactBase, MojangAssets, MojangLibrary, MojangArtifact, MojangLibraryDownloads, \ + Library, MetaVersionFile, GradleSpecifier + +SUPPORTED_LAUNCHER_VERSION = 21 +SUPPORTED_COMPLIANCE_LEVEL = 1 ''' Mojang index files look like this: @@ -68,3 +72,133 @@ class ExperimentIndexWrap: def __init__(self, index: ExperimentIndex): self.index: ExperimentIndex = index self.versions: Dict[str, ExperimentEntry] = dict((x.id, x) for x in index.experiments) + + +class LegacyOverrideEntry(MetaBase): + main_class: Optional[str] = Field(alias="mainClass") + applet_class: Optional[str] = Field(alias="appletClass") + release_time: Optional[datetime] = Field(alias="releaseTime") + additional_traits: Optional[List[str]] = Field(alias="+traits") + + def apply_onto_meta_version(self, meta_version: MetaVersionFile, legacy: bool = True): + # simply hard override classes + meta_version.main_class = self.main_class + meta_version.applet_class = self.applet_class + # if we have an updated release time (more correct than Mojang), use it + if self.release_time: + meta_version.release_time = self.release_time + + # add traits, if any + if self.additional_traits: + if not meta_version.additional_traits: + meta_version.additional_traits = [] + meta_version.additional_traits = meta_version.additional_traits + self.additional_traits + + if legacy: + # remove all libraries - they are not needed for legacy + meta_version.libraries = None + # remove minecraft arguments - we use our own hardcoded ones + meta_version.minecraft_arguments = None + + +class LegacyOverrideIndex(MetaBase): + versions: Dict[str, LegacyOverrideEntry] + + +class MojangArguments(MetaBase): + game: Optional[List[Any]] # mixture of strings and objects + jvm: Optional[List[Any]] + + +class MojangLoggingArtifact(MojangArtifactBase): + id: str + + +class MojangLogging(MetaBase): + @validator("type") + def validate_type(cls, v): + assert v in ["log4j2-xml"] + return v + + file: MojangLoggingArtifact + argument: str + type: str + + +class JavaVersion(MetaBase): + component: str = "jre-legacy" + majorVersion: int = 8 + + +class MojangVersionFile(MetaBase): + @validator("minimumLauncherVersion") + def validate_minimum_launcher_version(cls, v): + assert v <= SUPPORTED_LAUNCHER_VERSION + return v + + @validator("complianceLevel") + def validate_compliance_level(cls, v): + assert v <= SUPPORTED_COMPLIANCE_LEVEL + return v + + id: str # TODO: optional? + arguments: Optional[MojangArguments] + assetIndex: Optional[MojangAssets] + assets: Optional[str] + downloads: Dict[str, MojangArtifactBase] # TODO improve this? + libraries: Optional[List[MojangLibrary]] # TODO: optional? + mainClass: Optional[str] + appletClass: Optional[str] + processArguments: Optional[str] + minecraftArguments: Optional[str] + minimumLauncherVersion: Optional[int] # TODO: validate validateSupportedMojangVersion + releaseTime: Optional[datetime] + time: Optional[datetime] + type: Optional[str] + inheritsFrom: Optional[str] + logging: Optional[Dict[str, MojangLogging]] # TODO improve this? + complianceLevel: Optional[int] + javaVersion: Optional[JavaVersion] + + def to_meta_version(self, name: str, uid: str, version: str) -> MetaVersionFile: + main_jar = None + addn_traits = None + new_type = self.type + compatible_java_majors = None + if self.id: + client_download = self.downloads['client'] + artifact = MojangArtifact(url=client_download.url, sha1=client_download.sha1, size=client_download.size) + downloads = MojangLibraryDownloads(artifact=artifact) + main_jar = Library(name=GradleSpecifier("com.mojang:minecraft:%s:client" % self.id), downloads=downloads) + + if not self.complianceLevel: # both == 0 and is None + pass + elif self.complianceLevel == 1: + if not addn_traits: + addn_traits = [] + addn_traits.append("XR:Initial") + else: + raise Exception(f"Unsupported compliance level {self.complianceLevel}") + + if self.javaVersion is not None: # some versions don't have this. TODO: maybe maintain manual overrides + major = self.javaVersion.major_version + compatible_java_majors = [major] + if major == 16: # TODO: deal with this somewhere else + compatible_java_majors.append(17) + + if new_type == "pending": # TODO: why wasn't this needed before large refactor + new_type = "experiment" + + return MetaVersionFile( + name=name, + uid=uid, + version=version, + asset_index=self.assetIndex, + libraries=self.libraries, + main_class=self.mainClass, + minecraft_arguments=self.minecraftArguments, + release_time=self.releaseTime, + type=new_type, + compatible_java_majors=compatible_java_majors, + additional_traits=addn_traits, + main_jar=main_jar) -- cgit 0.0.5-2-1-g0f52