mirror of
https://github.com/unmojang/meta.git
synced 2025-09-27 21:23:43 -04:00
364 lines
9.7 KiB
Python
364 lines
9.7 KiB
Python
import copy
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Optional, List, Dict, Any, Iterator
|
|
|
|
import pydantic
|
|
from pydantic import Field, validator # type: ignore
|
|
|
|
from ..common import (
|
|
LAUNCHER_MAVEN,
|
|
serialize_datetime,
|
|
replace_old_launchermeta_url,
|
|
get_all_bases,
|
|
merge_dict,
|
|
)
|
|
|
|
META_FORMAT_VERSION = 1
|
|
|
|
|
|
class GradleSpecifier:
|
|
"""
|
|
A gradle specifier - a maven coordinate. Like one of these:
|
|
"org.lwjgl.lwjgl:lwjgl:2.9.0"
|
|
"net.java.jinput:jinput:2.0.5"
|
|
"net.minecraft:launchwrapper:1.5"
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
group: str,
|
|
artifact: str,
|
|
version: str,
|
|
classifier: Optional[str] = None,
|
|
extension: Optional[str] = None,
|
|
):
|
|
if extension is None:
|
|
extension = "jar"
|
|
self.group = group
|
|
self.artifact = artifact
|
|
self.version = version
|
|
self.classifier = classifier
|
|
self.extension = extension
|
|
|
|
def __str__(self):
|
|
ext = ""
|
|
if self.extension != "jar":
|
|
ext = "@%s" % self.extension
|
|
if self.classifier:
|
|
return "%s:%s:%s:%s%s" % (
|
|
self.group,
|
|
self.artifact,
|
|
self.version,
|
|
self.classifier,
|
|
ext,
|
|
)
|
|
else:
|
|
return "%s:%s:%s%s" % (self.group, self.artifact, self.version, ext)
|
|
|
|
def filename(self):
|
|
if self.classifier:
|
|
return "%s-%s-%s.%s" % (
|
|
self.artifact,
|
|
self.version,
|
|
self.classifier,
|
|
self.extension,
|
|
)
|
|
else:
|
|
return "%s-%s.%s" % (self.artifact, self.version, self.extension)
|
|
|
|
def base(self):
|
|
return "%s/%s/%s/" % (self.group.replace(".", "/"), self.artifact, self.version)
|
|
|
|
def path(self):
|
|
return self.base() + self.filename()
|
|
|
|
def __repr__(self):
|
|
return f"GradleSpecifier('{self}')"
|
|
|
|
def is_lwjgl(self):
|
|
return self.group in (
|
|
"org.lwjgl",
|
|
"org.lwjgl.lwjgl",
|
|
"net.java.jinput",
|
|
"net.java.jutils",
|
|
)
|
|
|
|
def is_log4j(self):
|
|
return self.group == "org.apache.logging.log4j"
|
|
|
|
def __eq__(self, other: Any):
|
|
if isinstance(other, GradleSpecifier):
|
|
return str(self) == str(other)
|
|
else:
|
|
return False
|
|
|
|
def __lt__(self, other: "GradleSpecifier"):
|
|
return str(self) < str(other)
|
|
|
|
def __gt__(self, other: "GradleSpecifier"):
|
|
return str(self) > str(other)
|
|
|
|
def __hash__(self):
|
|
return hash(str(self))
|
|
|
|
@classmethod
|
|
def __get_validators__(cls):
|
|
yield cls.validate
|
|
|
|
@classmethod
|
|
def from_string(cls, v: str):
|
|
ext_split = v.split("@")
|
|
|
|
components = ext_split[0].split(":")
|
|
group = components[0]
|
|
artifact = components[1]
|
|
version = components[2]
|
|
|
|
extension = None
|
|
if len(ext_split) == 2:
|
|
extension = ext_split[1]
|
|
|
|
classifier = None
|
|
if len(components) == 4:
|
|
classifier = components[3]
|
|
return cls(group, artifact, version, classifier, extension)
|
|
|
|
@classmethod
|
|
def validate(cls, v: "str | GradleSpecifier"):
|
|
if isinstance(v, cls):
|
|
return v
|
|
if isinstance(v, str):
|
|
return cls.from_string(v)
|
|
raise TypeError("Invalid type")
|
|
|
|
|
|
class MetaBase(pydantic.BaseModel):
|
|
def dict(self, **kwargs: Any) -> Dict[str, Any]:
|
|
for k in ["by_alias"]:
|
|
if k in kwargs:
|
|
del kwargs[k]
|
|
|
|
return super(MetaBase, self).dict(by_alias=True, **kwargs)
|
|
|
|
def json(self, **kwargs: Any) -> str:
|
|
for k in ["exclude_none", "sort_keys", "indent"]:
|
|
if k in kwargs:
|
|
del kwargs[k]
|
|
|
|
return super(MetaBase, self).json(
|
|
exclude_none=True, sort_keys=True, by_alias=True, indent=4, **kwargs
|
|
)
|
|
|
|
def write(self, file_path: str):
|
|
Path(file_path).parent.mkdir(parents=True, exist_ok=True)
|
|
with open(file_path, "w") as f:
|
|
f.write(self.json())
|
|
|
|
def merge(self, other: "MetaBase"):
|
|
"""
|
|
Merge other object with self.
|
|
- Concatenates lists
|
|
- Combines sets
|
|
- Merges dictionaries (other takes priority)
|
|
- Recurses for all fields that are also MetaBase classes
|
|
- Overwrites for any other field type (int, string, ...)
|
|
"""
|
|
assert type(other) is type(self)
|
|
for key, field in self.__fields__.items():
|
|
ours = getattr(self, key)
|
|
theirs = getattr(other, key)
|
|
if theirs is None:
|
|
continue
|
|
if ours is None:
|
|
setattr(self, key, theirs)
|
|
continue
|
|
|
|
if isinstance(ours, list):
|
|
ours += theirs
|
|
elif isinstance(ours, set):
|
|
ours |= theirs
|
|
elif isinstance(ours, dict):
|
|
result = merge_dict(ours, copy.deepcopy(theirs)) # type: ignore
|
|
setattr(self, key, result)
|
|
elif MetaBase in get_all_bases(field.type_):
|
|
ours.merge(theirs)
|
|
else:
|
|
setattr(self, key, theirs)
|
|
|
|
def __hash__(self): # type: ignore
|
|
return hash(self.json())
|
|
|
|
class Config:
|
|
allow_population_by_field_name = True
|
|
|
|
json_encoders = {datetime: serialize_datetime, GradleSpecifier: str}
|
|
|
|
|
|
class Versioned(MetaBase):
|
|
@validator("format_version")
|
|
def format_version_must_be_supported(cls, v: int):
|
|
assert v <= META_FORMAT_VERSION
|
|
return v
|
|
|
|
format_version: int = Field(META_FORMAT_VERSION, alias="formatVersion")
|
|
|
|
|
|
class MojangArtifactBase(MetaBase):
|
|
sha1: Optional[str]
|
|
size: Optional[int]
|
|
url: str
|
|
|
|
|
|
class MojangAssets(MojangArtifactBase):
|
|
@validator("url")
|
|
def validate_url(cls, v: str):
|
|
return replace_old_launchermeta_url(v)
|
|
|
|
id: str
|
|
totalSize: int
|
|
|
|
|
|
class MojangArtifact(MojangArtifactBase):
|
|
path: Optional[str]
|
|
|
|
|
|
class MojangLibraryExtractRules(MetaBase):
|
|
"""
|
|
"rules": [
|
|
{
|
|
"action": "allow"
|
|
},
|
|
{
|
|
"action": "disallow",
|
|
"os": {
|
|
"name": "osx"
|
|
}
|
|
}
|
|
]
|
|
"""
|
|
|
|
exclude: List[str] # TODO maybe drop this completely?
|
|
|
|
|
|
class MojangLibraryDownloads(MetaBase):
|
|
artifact: Optional[MojangArtifact]
|
|
classifiers: Optional[Dict[Any, MojangArtifact]]
|
|
|
|
|
|
class OSRule(MetaBase):
|
|
@validator("name")
|
|
def name_must_be_os(cls, v: str):
|
|
assert v in [
|
|
"osx",
|
|
"linux",
|
|
"windows",
|
|
"windows-arm64",
|
|
"osx-arm64",
|
|
"linux-arm64",
|
|
"linux-arm32",
|
|
]
|
|
return v
|
|
|
|
name: str
|
|
version: Optional[str]
|
|
|
|
|
|
class MojangRule(MetaBase):
|
|
@validator("action")
|
|
def action_must_be_allow_disallow(cls, v: str):
|
|
assert v in ["allow", "disallow"]
|
|
return v
|
|
|
|
action: str
|
|
os: Optional[OSRule]
|
|
|
|
|
|
class MojangRules(MetaBase):
|
|
__root__: List[MojangRule]
|
|
|
|
def __iter__(self) -> Iterator[MojangRule]: # type: ignore
|
|
return iter(self.__root__)
|
|
|
|
def __getitem__(self, item: int) -> MojangRule:
|
|
return self.__root__[item]
|
|
|
|
|
|
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 Library(MetaBase):
|
|
extract: Optional[MojangLibraryExtractRules]
|
|
name: Optional[GradleSpecifier]
|
|
downloads: Optional[MojangLibraryDownloads]
|
|
natives: Optional[Dict[str, str]]
|
|
rules: Optional[MojangRules]
|
|
url: Optional[str]
|
|
absoluteUrl: Optional[str] = Field(None, alias="MMC-absoluteUrl")
|
|
mmcHint: Optional[str] = Field(None, alias="MMC-hint")
|
|
|
|
|
|
class Dependency(MetaBase):
|
|
uid: str
|
|
equals: Optional[str]
|
|
suggests: Optional[str]
|
|
|
|
|
|
class Agent(Library):
|
|
argument: Optional[str]
|
|
|
|
|
|
class MetaVersion(Versioned):
|
|
name: str
|
|
version: str
|
|
uid: str
|
|
type: Optional[str]
|
|
order: Optional[int]
|
|
volatile: Optional[bool]
|
|
requires: Optional[List[Dependency]]
|
|
conflicts: Optional[List[Dependency]]
|
|
libraries: Optional[List[Library]]
|
|
asset_index: Optional[MojangAssets] = Field(alias="assetIndex")
|
|
maven_files: Optional[List[Library]] = Field(alias="mavenFiles")
|
|
main_jar: Optional[Library] = Field(alias="mainJar")
|
|
jar_mods: Optional[List[Library]] = Field(alias="jarMods")
|
|
main_class: Optional[str] = Field(alias="mainClass")
|
|
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")
|
|
compatible_java_name: Optional[str] = Field(alias="compatibleJavaName")
|
|
additional_traits: Optional[List[str]] = Field(alias="+traits")
|
|
additional_tweakers: Optional[List[str]] = Field(alias="+tweakers")
|
|
additional_jvm_args: Optional[List[str]] = Field(alias="+jvmArgs")
|
|
additional_agents: Optional[List[Agent]] = Field(alias="+agents")
|
|
logging: Optional[MojangLogging]
|
|
|
|
|
|
class MetaPackage(Versioned):
|
|
name: str
|
|
uid: str
|
|
recommended: Optional[List[str]]
|
|
authors: Optional[List[str]]
|
|
description: Optional[str]
|
|
project_url: Optional[str] = Field(alias="projectUrl")
|
|
|
|
|
|
def make_launcher_library(
|
|
name: GradleSpecifier, hash: str, size: int, maven=LAUNCHER_MAVEN
|
|
):
|
|
artifact = MojangArtifact(url=maven % name.path(), sha1=hash, size=size)
|
|
return Library(name=name, downloads=MojangLibraryDownloads(artifact=artifact))
|