Table of Contents

Class ModrinthService

Namespace
McServerLauncher.Services
Assembly
McServerLauncher.dll

The app's whole conversation with Modrinth: searching the store, reading a project, resolving a version for this server, identifying installed jars by hash, and downloading a file.

public class ModrinthService
Inheritance
ModrinthService
Inherited Members

Remarks

Every query is already narrowed by the server's loader and game version, because a result the server cannot run is worse than no result: it installs, and then the server does not come up. The loader a type browses under comes from ServerTypeCatalog, so Purpur searching Paper's plugins is a property of the table rather than a branch in here.

Reads go through StoreCache with a per-endpoint freshness window (see the constants below), which is what makes opening a mod, going back and opening it again cost one request — and what makes a project already seen open with no connection at all.

The hash endpoints answer two different questions off the same scan, and the difference matters: GetVersionsByHashAsync(IEnumerable<string>, CancellationToken) says what each jar is (its project and its declared dependencies), while GetLatestVersionsByHashAsync(IEnumerable<string>, ServerType, string, CancellationToken) says what could replace it. The update check needs the second; the missing-library check needs the first.

Methods

DownloadModAsync(string, string, string?, string?, IProgress<double>?, CancellationToken)

Downloads a mod/plugin file from Modrinth. Mods are third-party jars chosen by the user, so the download is verified against the hash Modrinth publishes for it; a mismatch deletes the file and throws instead of installing it. Sha512 is preferred (stronger); Sha1 is used only if Modrinth didn't provide a Sha512 for this file.

public virtual Task DownloadModAsync(string downloadUrl, string destinationPath, string? expectedSha512 = null, string? expectedSha1 = null, IProgress<double>? progress = null, CancellationToken ct = default)

Parameters

downloadUrl string
destinationPath string
expectedSha512 string
expectedSha1 string
progress IProgress<double>
ct CancellationToken

Returns

Task

Remarks

Neither hash means no install. Modrinth always publishes one, so its absence is an answer the app does not understand rather than a normal case — and the rule written down for mods (see VerifiedJarDownload) was "no checksum, no install", while this went ahead and installed the jar unverified.

GetLatestProjectVersionAsync(string, ServerType, string, CancellationToken)

public virtual Task<VersionResult?> GetLatestProjectVersionAsync(string projectId, ServerType loader, string mcVersion, CancellationToken ct = default)

Parameters

projectId string
loader ServerType
mcVersion string
ct CancellationToken

Returns

Task<VersionResult>

GetLatestVersionsByHashAsync(IEnumerable<string>, ServerType, string, CancellationToken)

Given the SHA-1 of each installed jar, asks Modrinth (in a single request) for the latest version of each corresponding project that is compatible with this server's loader and Minecraft version. Returns a map keyed by the SAME input hash the caller passed. Hashes that Modrinth doesn't recognise (jars from CurseForge or built by hand) are simply absent from the result.

public virtual Task<Dictionary<string, VersionResult>?> GetLatestVersionsByHashAsync(IEnumerable<string> sha1Hashes, ServerType loader, string mcVersion, CancellationToken ct = default)

Parameters

sha1Hashes IEnumerable<string>
loader ServerType
mcVersion string
ct CancellationToken

Returns

Task<Dictionary<string, VersionResult>>

The map, or null when Modrinth could not be asked. Deliberately not an empty map: that is what "every mod is on its newest version" looks like, and a failure that reads as good news is one nobody ever reports. The upper-case hashes fixed in 1.12.3 hid for exactly that reason, and "no connection" was taking the same way out.

GetProjectAsync(string, CancellationToken)

Full project: long description, gallery, links and licence.

public Task<ProjectDetail?> GetProjectAsync(string idOrSlug, CancellationToken ct = default)

Parameters

idOrSlug string
ct CancellationToken

Returns

Task<ProjectDetail>

GetProjectVersionsAsync(string, ServerType, string?, CancellationToken)

Every published version of a project, newest first. When mcVersion is given the list is narrowed to what runs on this server's loader and Minecraft version — the same filter the installer uses, so what the details page offers is what actually installs.

public Task<List<VersionResult>?> GetProjectVersionsAsync(string projectId, ServerType loader, string? mcVersion = null, CancellationToken ct = default)

Parameters

projectId string
loader ServerType
mcVersion string
ct CancellationToken

Returns

Task<List<VersionResult>>

GetProjectsAsync(IEnumerable<string>, CancellationToken)

Several projects in a single request. Used for dependencies and related mods, so opening a mod with five dependencies costs one request rather than five.

public Task<List<ProjectDetail>?> GetProjectsAsync(IEnumerable<string> ids, CancellationToken ct = default)

Parameters

ids IEnumerable<string>
ct CancellationToken

Returns

Task<List<ProjectDetail>>

GetTeamMembersAsync(string, CancellationToken)

The project's team, which is where the author names live.

public Task<List<TeamMember>?> GetTeamMembersAsync(string teamId, CancellationToken ct = default)

Parameters

teamId string
ct CancellationToken

Returns

Task<List<TeamMember>>

GetVersionAsync(string, CancellationToken)

One exact version, by its Modrinth id. Used for dependencies that pin a version rather than naming a project, where "the newest compatible one" would be the wrong answer.

public Task<VersionResult?> GetVersionAsync(string versionId, CancellationToken ct = default)

Parameters

versionId string
ct CancellationToken

Returns

Task<VersionResult>

GetVersionsByHashAsync(IEnumerable<string>, CancellationToken)

Which Modrinth version each installed jar actually is, keyed by the SHA-1 that was passed in.

public virtual Task<Dictionary<string, VersionResult>> GetVersionsByHashAsync(IEnumerable<string> sha1Hashes, CancellationToken ct = default)

Parameters

sha1Hashes IEnumerable<string>
ct CancellationToken

Returns

Task<Dictionary<string, VersionResult>>

Remarks

Unlike GetLatestVersionsByHashAsync(IEnumerable<string>, ServerType, string, CancellationToken) this asks what a jar is, not what could replace it — so the answer carries the project id and the dependency list of the file on disk. That is what makes it possible to tell a server that is missing a library mod from one that is complete. Jars Modrinth does not know are simply absent from the result.

SearchModsAsync(string, ServerType, string, string, int, int, IReadOnlyCollection<string>?, StoreSideFilter, CancellationToken)

Searches Modrinth for content compatible with a server. extraFacetGroups adds already-formatted facet groups (e.g. ["categories:optimization"]) that are ANDed with the loader/version/type filter — this is what the category chips use. side picks the projects by where they run; plugins are always server-side.

public virtual Task<SearchResponse?> SearchModsAsync(string query, ServerType loader, string mcVersion, string index = "relevance", int offset = 0, int limit = 20, IReadOnlyCollection<string>? extraFacetGroups = null, StoreSideFilter side = StoreSideFilter.All, CancellationToken ct = default)

Parameters

query string
loader ServerType
mcVersion string
index string
offset int
limit int
extraFacetGroups IReadOnlyCollection<string>
side StoreSideFilter
ct CancellationToken

Returns

Task<SearchResponse>