current status
There is one downloader, SpaceDockModDownloader, and it is built around SpaceDock's API: it resolves the id through SpaceDockResolver, calls api/mod/{id}, picks a version row, and takes the download path from it.
Every release in the index today points at GitHub, with SpaceDock only as a mirror:
AdvancedFlightComputer 0.7.3 github.com/.../releases/download/v0.7.3/AdvancedFlightComputer.zip
MeasureTools 1.1.8 github.com/.../releases/download/v1.1.8/MeasureTools.zip
StarMap 0.4.6 github.com/StarMapLoader/StarMap/releases/download/0.4.6/StarMap-0.4.6.zip
So Borea cannot install a single entry from its own index.
One host indepentend downloader should be enough
All the host-specific work already happened at stamp time.
download.url is absolute, download.mirrors lists the alternates, and even SpaceDockModRepository builds an absolute URL out of version.DownloadPath before anybody downloads anything.
What it should be instead
| Today |
Should be |
SpaceDockModDownloader is the only implementation, and it re-queries api/mod/{id} for a path the repository already resolved. |
One host-agnostic downloader that fetches Download.Url. GitHub, SpaceDock and any later host need no code of their own. |
DownloadAsync gets an id and a version, so it has to ask a host again for the URL. |
It gets the release, because ModVersionMetadata.Download already carries the URL, the hash, the size and the content type. |
DownloadResult.Checksum is computed and then compared against nothing. |
The bytes are checked against DownloadInfo.Sha256 through HashMatches before anything is written, and a mismatch fails the install rather than warning. |
| One URL, so a dead link is a dead end. |
Download.Mirrors is tried next. Every AdvancedFlightComputer and MeasureTools release in the index already carries its SpaceDock mirror, and RFC 0031 accepts any source whose bytes match the hash. |
ResolveContentRoot guesses: one top-level directory and no top-level files wins, otherwise the archive root. |
InstallInfo.Root decides, and null means the archive root per RFC 0035 rule 9. |
The installed folder is named by whatever the caller passed as destinationDirectory. |
The folder is named by the id, because Mod.MakeUsing assigns the id from the folder name and overwrites whatever the archive declared. |
ModTomlReader.ReadModId recovers the identity from the archive after the copy. |
The id is known before the download starts, from the listing. |
Nothing records the install. The DownloadResult doc says a caller builds an InstalledMod from it, and no such caller exists. |
The install writes an InstalledMod with the version, the checksum and the InstallReason into the Instance, which is the only place a mod version will ever be recorded, since nothing on disk carries one. |
Three things the table does not cover
- Installing is not enabling. A new folder becomes a disabled manifest entry through
ModLibrary.AddMods, and the manifest rules are their own issue.
- Uninstall stays symmetric and removes only what the install wrote.
IModUninstaller says itself that it does not check dependencies, so the caller asks ModDependencyResolver.CheckUninstall first.
- Progress and cancellation already work through
DownloadProgress and the token, and I would keep both as they are.
Tests
A GitHub URL and a SpaceDock URL through the same code path, a hash mismatch, a mirror that saves the install, a release with no hash at all, an authored install.root, and a top-level folder that disagrees with the id.
Not in scope: any UI, installing a loader, installing a pack, and the manifest.toml rules.
Depends on
#44, for a repository that can return a release file at all.
#41 is done, so InstallInfo already carries Root, Target and Path since #45 and #46 merged.
current status
There is one downloader,
SpaceDockModDownloader, and it is built around SpaceDock's API: it resolves the id throughSpaceDockResolver, callsapi/mod/{id}, picks a version row, and takes the download path from it.Every release in the index today points at GitHub, with SpaceDock only as a mirror:
So Borea cannot install a single entry from its own index.
One host indepentend downloader should be enough
All the host-specific work already happened at stamp time.
download.urlis absolute,download.mirrorslists the alternates, and evenSpaceDockModRepositorybuilds an absolute URL out ofversion.DownloadPathbefore anybody downloads anything.What it should be instead
SpaceDockModDownloaderis the only implementation, and it re-queriesapi/mod/{id}for a path the repository already resolved.Download.Url. GitHub, SpaceDock and any later host need no code of their own.DownloadAsyncgets an id and a version, so it has to ask a host again for the URL.ModVersionMetadata.Downloadalready carries the URL, the hash, the size and the content type.DownloadResult.Checksumis computed and then compared against nothing.DownloadInfo.Sha256throughHashMatchesbefore anything is written, and a mismatch fails the install rather than warning.Download.Mirrorsis tried next. Every AdvancedFlightComputer and MeasureTools release in the index already carries its SpaceDock mirror, and RFC 0031 accepts any source whose bytes match the hash.ResolveContentRootguesses: one top-level directory and no top-level files wins, otherwise the archive root.InstallInfo.Rootdecides, and null means the archive root per RFC 0035 rule 9.destinationDirectory.Mod.MakeUsingassigns the id from the folder name and overwrites whatever the archive declared.ModTomlReader.ReadModIdrecovers the identity from the archive after the copy.DownloadResultdoc says a caller builds anInstalledModfrom it, and no such caller exists.InstalledModwith the version, the checksum and theInstallReasoninto theInstance, which is the only place a mod version will ever be recorded, since nothing on disk carries one.Three things the table does not cover
ModLibrary.AddMods, and the manifest rules are their own issue.IModUninstallersays itself that it does not check dependencies, so the caller asksModDependencyResolver.CheckUninstallfirst.DownloadProgressand the token, and I would keep both as they are.Tests
A GitHub URL and a SpaceDock URL through the same code path, a hash mismatch, a mirror that saves the install, a release with no hash at all, an authored
install.root, and a top-level folder that disagrees with the id.Not in scope: any UI, installing a loader, installing a pack, and the
manifest.tomlrules.Depends on
#44, for a repository that can return a release file at all.#41is done, soInstallInfoalready carriesRoot,TargetandPathsince #45 and #46 merged.