Documentation Index
Fetch the complete documentation index at: https://mintlify.com/CollapseLauncher/Collapse/llms.txt
Use this file to discover all available pages before exploring further.
Lifecycle overview
A plugin progresses through the following states from the perspective ofPluginInfo and PluginManager:
Phase 1: Discovery and pending routines
At launcher startup,PluginManager.LoadPlugins scans the plugin folder for subdirectories matching Hi3Helper.Plugin.*. Before any plugin is loaded, ApplyPendingRoutines runs once over the entire plugin folder.
_markPendingDeletion
If _markPendingDeletion exists in a plugin directory, ApplyPendingUninstallRoutine runs:
- Reads
manifest.jsonto obtain the asset list. - Deletes every file listed in
Assets[]. - Deletes
manifest.jsonitself. - Deletes
_markPendingDeletion. - Removes the directory if it is now empty.
_markPendingUpdate / _markPendingUpdateApply
If _markPendingUpdate/<plugin-dir>/_markPendingUpdateApply exists, ApplyPendingUpdateRoutine runs:
- Deletes
_markPendingUpdateApply. - Removes all files from the parent plugin directory that are not inside
_markPendingUpdate/. - Moves all files from
_markPendingUpdate/into the parent directory. - Cleans up the now-empty
_markPendingUpdate/staging directory.
Phase 2: Load or skip
For each plugin directory that has a validmanifest.json:
- If
_markDisabledexists, the plugin is constructed without loading the DLL (load = false). It appears in the Plugin Manager as disabled. No exports are resolved and no COM objects are created. - If
_markDisabledis absent, the plugin DLL is loaded and the full initialization sequence runs.
Phase 3: DLL loading and initialization
When loading the DLL:NativeLibrary.TryLoadmaps the DLL into the process. If it fails, the error is thrown and the plugin is skipped.- The four required exports are resolved. Missing any one aborts the load.
- The logger callback is injected via
SetLoggerCallback. GetPluginStandardVersionandGetPluginVersionare called. Their results are stored on thePluginInfoobject.GetPluginis called and the pointer is marshalled toIPlugin.IPlugin.GetPluginSelfUpdateris called to obtain an optionalIPluginSelfUpdatereference.GetPluginUpdateCdnListis optionally resolved and called to obtain the CDN URL list.IPlugin.GetPluginName,GetPluginDescription,GetPluginAuthor, andGetPluginCreationDateare called.IPlugin.SetPluginLocaleIdis called with the launcher’s current UI language.- All
IPluginPresetConfiginstances are enumerated and wrapped intoPluginPresetConfigWrapperobjects. - If the speed limiter is active,
RegisterSpeedThrottlerServiceis called. PluginInfo.Initializeis called, which sets up DNS resolvers (if configured) and callsIPluginPresetConfig.InitAsyncon each preset config.
Phase 4: Running
Once loaded and initialized, a plugin is “running”. Collapse calls into the plugin on demand:- Home page load —
PluginLauncherApiWrapper.LoadAsynccallsIGameManager.InitPluginComAsync,ILauncherApiMedia.InitPluginComAsync, andILauncherApiNews.InitPluginComAsyncin parallel. On completion, background images, news articles, carousels, and social media entries are converted from plugin types to Collapse’s internal structures. - Game state —
IGameManager.IsGameInstalled,IsGameHasUpdate, andIsGameHasPreloadare called whenever the launcher needs to refresh game status. - Launch —
IGameManagerprovides the path; the launcher’s game launch infrastructure invokes the game directly. - Locale change —
PluginManager.SetPluginLocaleIdpropagates the new locale to every loaded plugin viaIPlugin.SetPluginLocaleId.
Sentinel files
Sentinel files are plain text files placed in the plugin directory. Their presence or absence is the state mechanism for enable/disable and deferred operations.| File | Location | Meaning |
|---|---|---|
_markDisabled | <pluginDir>/ | Plugin is disabled. DLL not loaded. Removed when the user re-enables the plugin. |
_markPendingDeletion | <pluginDir>/ | Plugin is queued for uninstallation. Files are deleted on next launcher startup. |
_markPendingUpdate/ | <pluginDir>/ | Staging directory containing new plugin files downloaded during an update. |
_markPendingUpdateApply | <pluginDir>/_markPendingUpdate/ | Stamp file written when update download completes. Triggers ApplyPendingUpdateRoutine on next launch. |
Sentinel files contain their own name as content (e.g.,
_markDisabled contains the text _markDisabled). The launcher only checks for file existence, not content.Enabling and disabling
SettingPluginInfo.IsEnabled = false creates _markDisabled. Setting it to true deletes it. The change takes effect on the next launcher startup — a running plugin cannot be hot-reloaded. The Plugin Manager shows a restart-required indicator when IsChanged is set.
Marking for deletion
SettingPluginInfo.IsMarkedForDeletion = true creates _markPendingDeletion. The Plugin Manager allows restoring a plugin before the next launch by setting the value back to false, which deletes the sentinel.
Plugin updates
Updates can be driven in two ways, which Collapse tries in order:Managed update path (CDN)
RequiresGetPluginUpdateCdnList to return at least one URL.
- Collapse picks a random URL from the list and fetches
<url>/manifest.json. - The remote
PluginVersionis compared to the installed version. If equal, no update is available. - If an update is available, each asset in the remote manifest is downloaded to
<pluginDir>/_markPendingUpdate/<asset.FilePath>. - MD5 hashes are verified for every downloaded file. Up to five retries per file on error.
- On completion,
_markPendingUpdateApplyis written inside_markPendingUpdate/. - On next launch,
ApplyPendingUpdateRoutinemoves the staged files into place.
Unmanaged update path (IPluginSelfUpdate)
Used ifIPlugin.GetPluginSelfUpdater returns a non-null IPluginSelfUpdate.
IPluginSelfUpdate.TryPerformUpdateAsyncis called withcheckOnly = trueto check availability.- The returned
SelfUpdateReturnInfois inspected forUpdateIsAvailableand the new version. - If an update is available,
TryPerformUpdateAsyncis called again withcheckOnly = false. The plugin downloads its own files intooutputDir(_markPendingUpdate/). - The stamp file
_markPendingUpdateApplyis written by Collapse on success. - On next launch,
ApplyPendingUpdateRoutineapplies the staged files.
If both
GetPluginUpdateCdnList and IPluginSelfUpdate are available, Collapse tries the managed CDN path first. The unmanaged path is only used if the managed path returns false.Auto-update
WhenIsEnablePluginAutoUpdate is true in launcher settings, Collapse runs StartBackgroundUpdateTask on startup. This calls RunCheckUpdateTask and, if an update is found, immediately calls RunUpdateTask for each eligible plugin in parallel.
Logging via SetLoggerCallback
AfterSetLoggerCallback is called, the plugin receives a function pointer with this signature:
messageBuffer is a UTF-16 character array of length messageLength (not null-terminated). Log output appears in Collapse’s unified log with the plugin’s relative path as the logger name (<directoryName>/<dllName>).
When the plugin is disposed, Collapse calls SetLoggerCallback(nint.Zero). The plugin must treat a zero pointer as a signal to stop logging and release any references to the callback.
Speed throttler integration
When the user enables the download speed limiter in Collapse Settings, the launcher callsRegisterSpeedThrottlerService on every loaded plugin that exports it:
addBytesCallback— Call this with the number of bytes downloaded in each chunk. The callback may block (or return a task) if the throttle limit has been reached.getThrottleCallback— Query the current throttle byte-per-second limit.
RegisterSpeedThrottlerService(nint.Zero, nint.Zero) to deregister. Your plugin should check for zero pointers and skip throttle integration when they are null.
Disposal
When the launcher exits orUnloadPlugins is called, each PluginInfo is disposed in order:
DisableDnsResolver— passesnint.ZerotoSetDnsResolverCallbackandSetDnsResolverCallbackAsync.SetLoggerCallback(nint.Zero)— detaches the logger.- Each
PluginPresetConfigWrapper.Dispose— releases COM references forILauncherApiMedia,ILauncherApiNews,IGameManager,IGameInstaller, andIPluginPresetConfig. FreePlugin()— called if the export exists. This is the preferred teardown path.- If
FreePluginis absent,Instance?.Free()is called directly on theIPluginCOM object. ComMarshal<IPlugin>.TryReleaseComObject— decrements the COM reference count.NativeLibrary.Free(Handle)— unmaps the DLL from the process.- The logger GC handle is freed and the callback delegate is nullified.