Extend Modsmith#
Platforms, pack formats and mod loaders each have a base class, so you can add your own without changing Modsmith.
Add a mod loader#
Subclass Loader, or MetaLoader if the loader publishes a Fabric-style version JSON, and register it. create can then find it by name, and packs that use it work like any other.
from dataclasses import dataclass
from typing import ClassVar
import modsmith as ms
@ms.loaders.register
@dataclass(frozen=True)
class MyLoader(ms.loaders.MetaLoader):
kind: ClassVar[str] = "myloader"
default_maven: ClassVar[str] = "https://maven.example.com/"
meta_url: str = "https://meta.example.com/v1"
def profile_url(self) -> str:
return f"{self.meta_url}/{self.minecraft}/{self.version}/profile.json"
loader = ms.loaders.create("myloader", "1.0.0", "1.21.1")
Override expected_id if the version id can be worked out without network access, so that is_installed works offline.
Add a modpack format#
Subclass PackFormat. index_file is the file that identifies the format, and parse turns it into a Modpack.
from pathlib import Path
from typing import Any
class MyFormat(ms.packs.PackFormat):
name = "myformat"
index_file = "mypack.json"
def parse(self, index: dict[str, Any], path: Path) -> ms.packs.Modpack:
entries = tuple(
ms.packs.PackEntry(
path=ms.packs.safe_relative(item["path"]), urls=(item["url"],)
)
for item in index["files"]
)
return ms.packs.Modpack(
format=self.name,
name=index["name"],
version=index["version"],
minecraft=index["minecraft"],
loader_kind=index["loader"],
loader_version=index["loader_version"],
entries=entries,
archive=path,
)
ms.packs.register(MyFormat())
The most recently registered format is tried first. safe_relative stops a bad pack from writing outside the instance.
Add a platform#
Subclass Platform and implement search, get_project and iter_releases. fetch and list_releases then work on their own. Override resolve if packs refer to your platform by id.
from collections.abc import Iterator
from modsmith.platforms import Platform, Project, Release, SearchResults
class MyPlatform(Platform):
name = "myplatform"
def search(self, query, *, project_type="modpack", game_version=None,
loader=None, offset=0, limit=20) -> SearchResults:
return SearchResults((), 0, offset)
def get_project(self, ref, *, project_type="modpack") -> Project:
return Project(self.name, str(ref), str(ref), str(ref))
def iter_releases(self, project, *, game_version=None, loader=None,
release_only=False) -> Iterator[Release]:
yield from ()
platform = MyPlatform()
assert list(platform.iter_releases(platform.get_project("example"))) == []
platform.close()
Yield the newest release first, so that next finds the latest one, and read pages only as they are needed.