Axolotl desktop CI / guardrails (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
Add pack sync markers, tagged mod updates, parallel progress, JWT downloads and retry recovery. Include pending onboarding, about scene, compatibility data pack and download fixes.
1117 lines
31 KiB
TypeScript
1117 lines
31 KiB
TypeScript
/**
|
|
* All theseus API calls return serialized values (both return values and errors);
|
|
* So, for example, addDefaultInstance creates a blank instance object, where the Rust struct is serialized,
|
|
* and deserialized into a usable JS object.
|
|
*/
|
|
import type { Labrinth } from '@modrinth/api-client'
|
|
import {
|
|
clearPinnedContentViewPreferences,
|
|
type ContentItem,
|
|
type ContentOwner,
|
|
} from '@modrinth/ui'
|
|
import { invoke } from '@tauri-apps/api/core'
|
|
|
|
import { isOfflineMode } from '@/composables/useNetworkStatus'
|
|
import { buildGcCandidateChain, resolveAutoGcStrategy } from '@/helpers/gc/auto-selector'
|
|
import { collectGcContext } from '@/helpers/gc/context'
|
|
import { detectGcStrategy, GC_STRATEGY_DEFINITIONS } from '@/helpers/gc/strategies'
|
|
import type { GcContext, ResolvedGcStrategyId } from '@/helpers/gc/types'
|
|
import { setLastGcLaunchReport } from '@/helpers/gc-notice'
|
|
import { getInstanceMode, prepareHostedSession } from '@/helpers/hosted-packs'
|
|
import { AUTO_GC_PRESET_ARG } from '@/helpers/java-arguments'
|
|
import { get_jre, get_memory_status } from '@/helpers/jre.js'
|
|
import { get as getSettings } from '@/helpers/settings'
|
|
|
|
import type { InstallJobSnapshot } from './install'
|
|
import { removeInstanceCache } from './instance-cache'
|
|
import type {
|
|
CacheBehaviour,
|
|
ContentFile,
|
|
ContentFileProjectType,
|
|
GameInstance,
|
|
InstanceLoader,
|
|
} from './types'
|
|
|
|
export interface InstancePostUpgradeWarning {
|
|
code: import('./instance-upgrade').InstanceUpgradeIssueCode
|
|
contentId: string | null
|
|
relativePath: string | null
|
|
}
|
|
|
|
export interface InstancePostUpgradeNotice {
|
|
instanceId: string
|
|
upgradeJobId: string
|
|
targetGameVersion: string
|
|
consecutiveCleanLaunches: number
|
|
warnings: InstancePostUpgradeWarning[]
|
|
}
|
|
|
|
export async function get_post_upgrade_notice(
|
|
instanceId: string,
|
|
): Promise<InstancePostUpgradeNotice | null> {
|
|
return await invoke('plugin:instance|instance_get_post_upgrade_notice', { instanceId })
|
|
}
|
|
|
|
export async function dismiss_post_upgrade_notice(instanceId: string): Promise<void> {
|
|
await invoke('plugin:instance|instance_dismiss_post_upgrade_notice', { instanceId })
|
|
}
|
|
|
|
export async function remove(instanceId: string): Promise<void> {
|
|
await invoke('plugin:instance|instance_remove', { instanceId })
|
|
removeInstanceCache(instanceId)
|
|
clearPinnedContentViewPreferences(instanceId)
|
|
}
|
|
|
|
export interface CreateDirectLinkInstanceRequest {
|
|
name?: string | null
|
|
launcherType: 'HMCL' | 'PCL2' | 'PCL2CE' | 'Generic'
|
|
basePath: string
|
|
instanceFolder: string
|
|
instancePath?: string | null
|
|
}
|
|
|
|
/** Associate an existing `.minecraft/versions/<id>` directory in place. */
|
|
export async function create_direct_link(request: CreateDirectLinkInstanceRequest) {
|
|
return await invoke<GameInstance>('plugin:instance|instance_create_direct_link', { request })
|
|
}
|
|
|
|
export interface DirectLinkSyncReport {
|
|
imported: number
|
|
updated: number
|
|
removed: number
|
|
missing: number
|
|
errors: string[]
|
|
}
|
|
|
|
export type ExternalMinecraftRootMode = 'isolated' | 'shared'
|
|
|
|
export interface ExternalMinecraftRoot {
|
|
path: string
|
|
mode: ExternalMinecraftRootMode
|
|
}
|
|
|
|
export async function sync_direct_links(
|
|
roots: ExternalMinecraftRoot[],
|
|
): Promise<DirectLinkSyncReport> {
|
|
return await invoke<DirectLinkSyncReport>('plugin:instance|instance_sync_direct_links', {
|
|
roots,
|
|
})
|
|
}
|
|
|
|
export async function get(instanceId: string): Promise<GameInstance | null> {
|
|
return await invoke('plugin:instance|instance_get', { instanceId })
|
|
}
|
|
|
|
export async function get_many(instanceIds: string[]): Promise<GameInstance[]> {
|
|
return await invoke('plugin:instance|instance_get_many', { instanceIds })
|
|
}
|
|
|
|
export async function get_projects(
|
|
instanceId: string,
|
|
cacheBehaviour?: CacheBehaviour,
|
|
): Promise<Record<string, ContentFile>> {
|
|
return await invoke('plugin:instance|instance_get_projects', { instanceId, cacheBehaviour })
|
|
}
|
|
|
|
export async function get_installed_project_ids(instanceId: string): Promise<string[]> {
|
|
return await invoke('plugin:instance|instance_get_installed_project_ids', { instanceId })
|
|
}
|
|
|
|
export type CoreComponentKind = 'jar_mod' | 'replacement_jar' | 'agent'
|
|
|
|
export type CoreComponentSource = {
|
|
provider: string
|
|
projectId: string | null
|
|
versionId: string | null
|
|
fileId: string | null
|
|
pageUrl: string | null
|
|
}
|
|
|
|
export type CoreComponent = {
|
|
id: string
|
|
kind: CoreComponentKind
|
|
fileName: string
|
|
relativePath: string
|
|
enabled: boolean
|
|
removed: boolean
|
|
order: number
|
|
sha1: string | null
|
|
sha256: string | null
|
|
source: CoreComponentSource | null
|
|
targetGameVersion: string
|
|
createdAt: string
|
|
modifiedAt: string
|
|
failureReason: string | null
|
|
}
|
|
|
|
export type CoreJarPreview = {
|
|
outputPath: string
|
|
componentCount: number
|
|
replacementComponentId: string | null
|
|
entries: number
|
|
sha1: string
|
|
sha256: string
|
|
}
|
|
|
|
export type McArchiveCoreInstallResult =
|
|
| { state: 'installed'; component: CoreComponent }
|
|
| {
|
|
state: 'manual_download'
|
|
fileName: string
|
|
pageUrl: string | null
|
|
expectedSha256: string | null
|
|
}
|
|
|
|
export async function list_core_components(instanceId: string): Promise<CoreComponent[]> {
|
|
return await invoke('plugin:instance|instance_list_core_components', { instanceId })
|
|
}
|
|
|
|
export async function add_core_jar_mod(
|
|
instanceId: string,
|
|
sourcePath: string,
|
|
targetGameVersion: string,
|
|
source?: CoreComponentSource,
|
|
): Promise<CoreComponent> {
|
|
return await invoke('plugin:instance|instance_add_core_jar_mod', {
|
|
instanceId,
|
|
sourcePath,
|
|
targetGameVersion,
|
|
source,
|
|
})
|
|
}
|
|
|
|
export async function replace_core_jar(
|
|
instanceId: string,
|
|
sourcePath: string,
|
|
targetGameVersion: string,
|
|
source?: CoreComponentSource,
|
|
): Promise<CoreComponent> {
|
|
return await invoke('plugin:instance|instance_replace_core_jar', {
|
|
instanceId,
|
|
sourcePath,
|
|
targetGameVersion,
|
|
source,
|
|
})
|
|
}
|
|
|
|
export async function move_core_component(
|
|
instanceId: string,
|
|
componentId: string,
|
|
direction: -1 | 1,
|
|
): Promise<CoreComponent[]> {
|
|
return await invoke('plugin:instance|instance_move_core_component', {
|
|
instanceId,
|
|
componentId,
|
|
direction,
|
|
})
|
|
}
|
|
|
|
export async function set_core_component_enabled(
|
|
instanceId: string,
|
|
componentId: string,
|
|
enabled: boolean,
|
|
): Promise<CoreComponent> {
|
|
return await invoke('plugin:instance|instance_set_core_component_enabled', {
|
|
instanceId,
|
|
componentId,
|
|
enabled,
|
|
})
|
|
}
|
|
|
|
export async function remove_core_component(
|
|
instanceId: string,
|
|
componentId: string,
|
|
): Promise<void> {
|
|
await invoke('plugin:instance|instance_remove_core_component', { instanceId, componentId })
|
|
}
|
|
|
|
export async function restore_core_component(
|
|
instanceId: string,
|
|
componentId: string,
|
|
): Promise<CoreComponent> {
|
|
return await invoke('plugin:instance|instance_restore_core_component', {
|
|
instanceId,
|
|
componentId,
|
|
})
|
|
}
|
|
|
|
export async function preview_core_jar(instanceId: string): Promise<CoreJarPreview | null> {
|
|
return await invoke('plugin:instance|instance_preview_core_jar', { instanceId })
|
|
}
|
|
|
|
export async function install_mcarchive_modloader(
|
|
instanceId: string,
|
|
gameVersion: string,
|
|
): Promise<McArchiveCoreInstallResult> {
|
|
return await invoke('plugin:instance|instance_install_mcarchive_modloader', {
|
|
instanceId,
|
|
gameVersion,
|
|
})
|
|
}
|
|
|
|
export async function import_mcarchive_modloader(
|
|
instanceId: string,
|
|
gameVersion: string,
|
|
sourcePath: string,
|
|
): Promise<McArchiveCoreInstallResult> {
|
|
return await invoke('plugin:instance|instance_import_mcarchive_modloader', {
|
|
instanceId,
|
|
gameVersion,
|
|
sourcePath,
|
|
})
|
|
}
|
|
|
|
export type McArchiveContentInstallRequest = {
|
|
projectId: string
|
|
projectSlug: string
|
|
versionId: string
|
|
fileId: string
|
|
projectType: ContentFileProjectType
|
|
}
|
|
|
|
export type McArchiveContentInstallResult =
|
|
| { state: 'installed'; relativePath: string }
|
|
| {
|
|
state: 'manual_download'
|
|
fileName: string
|
|
pageUrl: string | null
|
|
expectedSha256: string | null
|
|
}
|
|
|
|
export type PlanetMinecraftContentInstallRequest = {
|
|
projectId: string
|
|
versionId: string
|
|
projectType: ContentFileProjectType
|
|
}
|
|
|
|
export type PlanetMinecraftContentInstallResult =
|
|
| { state: 'installed'; relativePath: string }
|
|
| { state: 'manual_download'; pageUrl: string; fileName: string | null }
|
|
|
|
export async function install_mcarchive_content(
|
|
instanceId: string,
|
|
request: McArchiveContentInstallRequest,
|
|
): Promise<McArchiveContentInstallResult> {
|
|
return await invoke('plugin:instance|instance_install_mcarchive_content', {
|
|
instanceId,
|
|
request,
|
|
})
|
|
}
|
|
|
|
export async function import_mcarchive_content(
|
|
instanceId: string,
|
|
request: McArchiveContentInstallRequest,
|
|
sourcePath: string,
|
|
): Promise<McArchiveContentInstallResult> {
|
|
return await invoke('plugin:instance|instance_import_mcarchive_content', {
|
|
instanceId,
|
|
request,
|
|
sourcePath,
|
|
})
|
|
}
|
|
|
|
export async function install_planet_minecraft_content(
|
|
instanceId: string,
|
|
request: PlanetMinecraftContentInstallRequest,
|
|
): Promise<PlanetMinecraftContentInstallResult> {
|
|
return await invoke('plugin:instance|instance_install_planet_minecraft_content', {
|
|
instanceId,
|
|
request,
|
|
})
|
|
}
|
|
|
|
export async function import_planet_minecraft_content(
|
|
instanceId: string,
|
|
request: PlanetMinecraftContentInstallRequest,
|
|
sourcePath: string,
|
|
): Promise<PlanetMinecraftContentInstallResult> {
|
|
return await invoke('plugin:instance|instance_import_planet_minecraft_content', {
|
|
instanceId,
|
|
request,
|
|
sourcePath,
|
|
})
|
|
}
|
|
|
|
export type InstanceInstallTarget = {
|
|
game_version: string
|
|
loader: string
|
|
}
|
|
|
|
export type InstanceInstallCandidate = {
|
|
id: string
|
|
name: string
|
|
icon_path?: string | null
|
|
game_version: string
|
|
loader: InstanceLoader
|
|
installed: boolean
|
|
compatible: boolean
|
|
}
|
|
|
|
export async function get_install_candidates(
|
|
projectId: string,
|
|
projectType: string,
|
|
targets: InstanceInstallTarget[],
|
|
): Promise<InstanceInstallCandidate[]> {
|
|
return await invoke('plugin:instance|instance_get_install_candidates', {
|
|
projectId,
|
|
projectType,
|
|
targets,
|
|
})
|
|
}
|
|
|
|
// Get content items with rich metadata for an instance
|
|
// Returns content items filtered to exclude modpack files (if linked),
|
|
// sorted alphabetically by project name
|
|
export async function get_content_items(
|
|
instanceId: string,
|
|
cacheBehaviour?: CacheBehaviour,
|
|
): Promise<ContentItem[]> {
|
|
const items = await invoke<ContentItem[]>('plugin:instance|instance_get_content_items', {
|
|
instanceId,
|
|
cacheBehaviour,
|
|
})
|
|
return items
|
|
}
|
|
|
|
export async function get_content_items_by_paths(
|
|
instanceId: string,
|
|
paths: string[],
|
|
cacheBehaviour?: CacheBehaviour,
|
|
): Promise<ContentItem[]> {
|
|
return await invoke<ContentItem[]>('plugin:instance|instance_get_content_items_by_paths', {
|
|
instanceId,
|
|
paths,
|
|
cacheBehaviour,
|
|
})
|
|
}
|
|
|
|
export type ContentOwnershipKind = 'pack_managed' | 'user_added' | 'local_discovered'
|
|
export type PackMemberMaterializationState = 'present' | 'pending_manual' | 'missing' | 'removed'
|
|
export type PackMemberOverrideKind = 'none' | 'disabled' | 'removed' | 'version'
|
|
|
|
export interface InstanceContentSnapshotItem {
|
|
fileId: string | null
|
|
entryId: string | null
|
|
memberId: string | null
|
|
ownershipKind: ContentOwnershipKind
|
|
materializationState: PackMemberMaterializationState
|
|
overrideKind: PackMemberOverrideKind
|
|
expectedRelativePath: string
|
|
required: boolean
|
|
projectType: string
|
|
provider: 'modrinth' | 'curseforge' | 'mcarchive' | null
|
|
providerProjectId: string | null
|
|
providerReleaseId: string | null
|
|
content: ContentItem | null
|
|
capabilities: {
|
|
canToggle: boolean
|
|
canDelete: boolean
|
|
canUpdate: boolean
|
|
canChangeVersion: boolean
|
|
canRestorePackDefault: boolean
|
|
}
|
|
dependency: {
|
|
autoDependency: boolean
|
|
requiredBy: Array<{
|
|
provider: 'modrinth' | 'curseforge' | 'mcarchive' | 'local'
|
|
projectId: string
|
|
releaseId: string
|
|
}>
|
|
requires: Array<{
|
|
provider: 'modrinth' | 'curseforge' | 'mcarchive' | 'local'
|
|
projectId: string
|
|
releaseId: string
|
|
}>
|
|
orphaned: boolean
|
|
} | null
|
|
}
|
|
|
|
export interface PendingManualDownload {
|
|
id: string
|
|
instanceId: string
|
|
packMemberId: string | null
|
|
contentEntryId: string | null
|
|
operationKind: 'pack_install' | 'pack_update' | 'content_install' | 'content_update'
|
|
operationTargetId: string | null
|
|
projectType: string
|
|
provider: 'modrinth' | 'curseforge' | 'mcarchive'
|
|
providerProjectId: string
|
|
providerReleaseId: string
|
|
fileName: string
|
|
websiteUrl: string | null
|
|
targetRelativePath: string
|
|
expectedSha1: string | null
|
|
expectedSize: number | null
|
|
expectedFingerprint: number | null
|
|
state: 'waiting' | 'matched' | 'imported' | 'error' | 'cancelled'
|
|
context: Record<string, unknown>
|
|
createdAt: string
|
|
modifiedAt: string
|
|
}
|
|
|
|
export interface InstanceContentSnapshot {
|
|
instanceId: string
|
|
revision: number
|
|
pack: {
|
|
name: string
|
|
iconPath: string | null
|
|
provider: 'modrinth' | 'curseforge' | 'mcarchive' | null
|
|
projectId: string | null
|
|
versionId: string | null
|
|
reconciled: boolean
|
|
canUpdate: boolean
|
|
metadata: LinkedModpackInfo | null
|
|
} | null
|
|
items: InstanceContentSnapshotItem[]
|
|
pendingManualDownloads: PendingManualDownload[]
|
|
warnings: Array<{
|
|
code: string
|
|
message: string
|
|
provider: 'modrinth' | 'curseforge' | 'mcarchive' | null
|
|
}>
|
|
}
|
|
|
|
export async function get_content_snapshot(instanceId: string): Promise<InstanceContentSnapshot> {
|
|
return await invoke('plugin:instance|instance_get_content_snapshot', { instanceId })
|
|
}
|
|
|
|
export async function refresh_content(instanceId: string): Promise<InstanceContentSnapshot> {
|
|
return await invoke('plugin:instance|instance_refresh_content', { instanceId })
|
|
}
|
|
|
|
export type ContentUpdateScope = 'user_added' | 'pack' | 'item'
|
|
|
|
export interface ContentUpdatePlan {
|
|
id: string
|
|
instanceId: string
|
|
revision: number
|
|
scope: ContentUpdateScope
|
|
actions: Array<{
|
|
contentId: string
|
|
relativePath: string | null
|
|
ownershipKind: ContentOwnershipKind
|
|
provider: 'modrinth' | 'curseforge' | 'mcarchive'
|
|
currentReleaseId: string | null
|
|
targetReleaseId: string
|
|
}>
|
|
warnings: string[]
|
|
}
|
|
|
|
export interface ContentUpdateResolution {
|
|
contentId: string
|
|
choice: 'keep_override' | 'restore_pack_default'
|
|
}
|
|
|
|
export async function plan_content_updates(
|
|
instanceId: string,
|
|
scope: ContentUpdateScope,
|
|
target?: string,
|
|
): Promise<ContentUpdatePlan> {
|
|
return await invoke('plugin:instance|instance_plan_content_updates', {
|
|
instanceId,
|
|
scope,
|
|
target,
|
|
})
|
|
}
|
|
|
|
export async function apply_content_update_plan(
|
|
planId: string,
|
|
resolutions: ContentUpdateResolution[] = [],
|
|
): Promise<InstanceContentSnapshot> {
|
|
return await invoke('plugin:instance|instance_apply_content_update_plan', {
|
|
planId,
|
|
resolutions,
|
|
})
|
|
}
|
|
|
|
// Linked modpack info returned from backend
|
|
export interface LinkedModpackInfo {
|
|
project: Labrinth.Projects.v2.Project
|
|
version: Labrinth.Versions.v2.Version
|
|
owner: ContentOwner | null
|
|
update:
|
|
| {
|
|
provider: 'modrinth'
|
|
project_id: string
|
|
current_version_id: string
|
|
target_version_id: string
|
|
}
|
|
| {
|
|
provider: 'curseforge'
|
|
project_id: number
|
|
current_file_id: number
|
|
target_file_id: number
|
|
}
|
|
| null
|
|
update_version: Labrinth.Versions.v2.Version | null
|
|
}
|
|
|
|
// Get linked modpack info for an instance
|
|
// Returns project, version, and owner information for the linked modpack,
|
|
// or null if the instance is not linked to a modpack
|
|
export async function get_linked_modpack_info(
|
|
instanceId: string,
|
|
cacheBehaviour?: CacheBehaviour,
|
|
): Promise<LinkedModpackInfo | null> {
|
|
return await invoke('plugin:instance|instance_get_linked_modpack_info', {
|
|
instanceId,
|
|
cacheBehaviour,
|
|
})
|
|
}
|
|
|
|
// Get content items that are part of the linked modpack
|
|
// Returns the modpack's dependencies as ContentItem list
|
|
// Returns empty array if the instance is not linked to a modpack
|
|
export async function get_linked_modpack_content(
|
|
instanceId: string,
|
|
cacheBehaviour?: CacheBehaviour,
|
|
): Promise<ContentItem[]> {
|
|
const items = await invoke<ContentItem[]>('plugin:instance|instance_get_linked_modpack_content', {
|
|
instanceId,
|
|
cacheBehaviour,
|
|
})
|
|
return items
|
|
}
|
|
|
|
// Convert a list of dependencies into ContentItems with rich metadata
|
|
export async function get_dependencies_as_content_items(
|
|
dependencies: Labrinth.Versions.v3.Dependency[],
|
|
cacheBehaviour?: CacheBehaviour,
|
|
): Promise<ContentItem[]> {
|
|
const items = await invoke<ContentItem[]>(
|
|
'plugin:instance|instance_get_dependencies_as_content_items',
|
|
{
|
|
dependencies,
|
|
cacheBehaviour,
|
|
},
|
|
)
|
|
return items
|
|
}
|
|
|
|
export async function get_full_path(instanceId: string): Promise<string> {
|
|
return await invoke('plugin:instance|instance_get_full_path', { instanceId })
|
|
}
|
|
|
|
export async function get_mod_full_path(instanceId: string, projectPath: string): Promise<string> {
|
|
return await invoke('plugin:instance|instance_get_mod_full_path', { instanceId, projectPath })
|
|
}
|
|
|
|
export interface JavaVersion {
|
|
parsed_version: number
|
|
version: string
|
|
architecture: string
|
|
path: string
|
|
}
|
|
|
|
export async function get_optimal_jre_key(instanceId: string): Promise<JavaVersion | null> {
|
|
return await invoke('plugin:instance|instance_get_optimal_jre_key', { instanceId })
|
|
}
|
|
|
|
export async function list(): Promise<GameInstance[]> {
|
|
return await invoke('plugin:instance|instance_list')
|
|
}
|
|
|
|
export type DailyPlaytime = {
|
|
date: string
|
|
played_seconds: number
|
|
session_count: number
|
|
top_instance_name?: string | null
|
|
}
|
|
|
|
export async function set_pinned(instanceId: string, pinned: boolean): Promise<GameInstance> {
|
|
return await invoke('plugin:instance|instance_set_pinned', { instanceId, pinned })
|
|
}
|
|
|
|
export async function get_daily_playtime(
|
|
startDate: string,
|
|
endDate: string,
|
|
): Promise<DailyPlaytime[]> {
|
|
return await invoke('plugin:instance|instance_get_daily_playtime', { startDate, endDate })
|
|
}
|
|
|
|
export type DailyPlaytimeEntry = {
|
|
instance_id: string
|
|
instance_name: string
|
|
played_seconds: number
|
|
session_count: number
|
|
}
|
|
|
|
export async function get_daily_playtime_details(date: string): Promise<DailyPlaytimeEntry[]> {
|
|
return await invoke('plugin:instance|instance_get_daily_playtime_details', { date })
|
|
}
|
|
|
|
export async function check_installed(instanceId: string, projectId: string): Promise<boolean> {
|
|
return await invoke('plugin:instance|instance_check_installed', { instanceId, projectId })
|
|
}
|
|
|
|
export async function update_all(instanceId: string): Promise<Record<string, string>> {
|
|
return await invoke('plugin:instance|instance_update_all', { instanceId })
|
|
}
|
|
|
|
// Updates a specified project
|
|
export async function update_project(instanceId: string, projectPath: string): Promise<string> {
|
|
return await invoke('plugin:instance|instance_update_project', { instanceId, projectPath })
|
|
}
|
|
|
|
export interface ResolutionPreferences {
|
|
game_versions?: string[]
|
|
loaders?: string[]
|
|
}
|
|
|
|
export interface ResolveContentRequest {
|
|
project_id: string
|
|
version_id?: string | null
|
|
content_type: Labrinth.Content.v3.ContentType
|
|
selected?: ResolutionPreferences
|
|
excluded_project_ids?: string[]
|
|
force_project_ids?: string[]
|
|
}
|
|
|
|
export interface ResolvedContent {
|
|
project_id: string
|
|
version_id: string
|
|
dependent_on_version_id?: string | null
|
|
required?: boolean
|
|
}
|
|
|
|
export interface ResolveContentPlan {
|
|
primary: ResolvedContent
|
|
dependencies: ResolvedContent[]
|
|
skipped: Array<{
|
|
project_id: string
|
|
version_id?: string | null
|
|
dependent_on_version_id?: string | null
|
|
reason: string
|
|
}>
|
|
}
|
|
|
|
export async function add_project_from_version(
|
|
instanceId: string,
|
|
versionId: string,
|
|
): Promise<string> {
|
|
return await invoke('plugin:instance|instance_add_project_from_version', {
|
|
instanceId,
|
|
versionId,
|
|
})
|
|
}
|
|
|
|
export async function install_project_with_dependencies(
|
|
instanceId: string,
|
|
request: ResolveContentRequest,
|
|
): Promise<ResolveContentPlan> {
|
|
return await invoke('plugin:instance|instance_install_project_with_dependencies', {
|
|
instanceId,
|
|
request,
|
|
})
|
|
}
|
|
|
|
export async function preview_project_with_dependencies(
|
|
instanceId: string,
|
|
request: ResolveContentRequest,
|
|
): Promise<ResolveContentPlan> {
|
|
return await invoke('plugin:instance|instance_preview_project_with_dependencies', {
|
|
instanceId,
|
|
request,
|
|
})
|
|
}
|
|
|
|
export async function preview_project_with_dependencies_for_target(
|
|
request: ResolveContentRequest,
|
|
gameVersion: string,
|
|
loader: string,
|
|
): Promise<ResolveContentPlan> {
|
|
return await invoke('plugin:instance|instance_preview_project_with_dependencies_for_target', {
|
|
request,
|
|
gameVersion,
|
|
loader,
|
|
})
|
|
}
|
|
|
|
export async function queue_project_with_dependencies(
|
|
instanceId: string,
|
|
request: ResolveContentRequest,
|
|
display: { title: string; iconUrl?: string | null },
|
|
): Promise<InstallJobSnapshot> {
|
|
return await invoke('plugin:instance|instance_queue_project_with_dependencies', {
|
|
instanceId,
|
|
request,
|
|
displayTitle: display.title,
|
|
displayIcon: display.iconUrl ?? null,
|
|
})
|
|
}
|
|
|
|
export async function switch_project_version_with_dependencies(
|
|
instanceId: string,
|
|
projectPath: string,
|
|
versionId: string,
|
|
): Promise<string> {
|
|
return await invoke('plugin:instance|instance_switch_project_version_with_dependencies', {
|
|
instanceId,
|
|
projectPath,
|
|
versionId,
|
|
})
|
|
}
|
|
|
|
// Import a world save into an instance
|
|
// Returns the imported world name
|
|
export async function import_world_save(
|
|
instanceId: string,
|
|
sourcePath: string,
|
|
innerBase?: string,
|
|
): Promise<string> {
|
|
return await invoke('plugin:instance|instance_import_world_save', {
|
|
instanceId,
|
|
sourcePath,
|
|
innerBase,
|
|
})
|
|
}
|
|
|
|
export async function install_datapack_to_world(
|
|
instanceId: string,
|
|
worldPath: string,
|
|
sourcePath: string,
|
|
innerBase?: string,
|
|
): Promise<string> {
|
|
return await invoke('plugin:instance|instance_install_datapack_to_world', {
|
|
instanceId,
|
|
worldPath,
|
|
sourcePath,
|
|
innerBase,
|
|
})
|
|
}
|
|
|
|
// Add a project to an instance from a path + project_type
|
|
// Returns a path to the new project file
|
|
export async function add_project_from_path(
|
|
instanceId: string,
|
|
projectPath: string,
|
|
projectType?: ContentFileProjectType,
|
|
innerBase?: string,
|
|
): Promise<string> {
|
|
return await invoke('plugin:instance|instance_add_project_from_path', {
|
|
instanceId,
|
|
projectPath,
|
|
projectType,
|
|
innerBase,
|
|
})
|
|
}
|
|
|
|
// Toggle disabling a project
|
|
export async function toggle_disable_project(
|
|
instanceId: string,
|
|
projectPath: string,
|
|
desiredEnabled?: boolean,
|
|
): Promise<string> {
|
|
return await invoke('plugin:instance|instance_toggle_disable_project', {
|
|
instanceId,
|
|
projectPath,
|
|
desiredEnabled,
|
|
})
|
|
}
|
|
|
|
export async function toggle_content_entry(
|
|
instanceId: string,
|
|
contentId: string,
|
|
desiredEnabled?: boolean,
|
|
): Promise<string> {
|
|
return await invoke('plugin:instance|instance_toggle_content_entry', {
|
|
instanceId,
|
|
contentId,
|
|
desiredEnabled,
|
|
})
|
|
}
|
|
|
|
export type ContentToggleResult = {
|
|
contentId: string
|
|
path: string
|
|
enabled: boolean
|
|
}
|
|
|
|
export async function toggle_content_entries(
|
|
instanceId: string,
|
|
contentIds: string[],
|
|
desiredEnabled?: boolean,
|
|
): Promise<ContentToggleResult[]> {
|
|
return await invoke('plugin:instance|instance_toggle_content_entries', {
|
|
instanceId,
|
|
contentIds,
|
|
desiredEnabled,
|
|
})
|
|
}
|
|
|
|
// Roll back an updated project to its previous file (kept as a .old backup)
|
|
export async function rollback_project(instanceId: string, projectPath: string): Promise<string> {
|
|
return await invoke('plugin:instance|instance_rollback_project', {
|
|
instanceId,
|
|
projectPath,
|
|
})
|
|
}
|
|
|
|
// Remove a project
|
|
export async function remove_project(instanceId: string, projectPath: string): Promise<void> {
|
|
return await invoke('plugin:instance|instance_remove_project', { instanceId, projectPath })
|
|
}
|
|
|
|
export async function remove_content_entry(instanceId: string, contentId: string): Promise<void> {
|
|
return await invoke('plugin:instance|instance_remove_content_entry', { instanceId, contentId })
|
|
}
|
|
|
|
export async function update_content_entry(instanceId: string, contentId: string): Promise<string> {
|
|
return await invoke('plugin:instance|instance_update_content_entry', { instanceId, contentId })
|
|
}
|
|
|
|
export async function switch_content_entry_version(
|
|
instanceId: string,
|
|
contentId: string,
|
|
versionId: string,
|
|
): Promise<string> {
|
|
return await invoke('plugin:instance|instance_switch_content_entry_version', {
|
|
instanceId,
|
|
contentId,
|
|
versionId,
|
|
})
|
|
}
|
|
|
|
export async function restore_pack_member_default(
|
|
instanceId: string,
|
|
memberId: string,
|
|
): Promise<string | null> {
|
|
return await invoke('plugin:instance|instance_restore_pack_member_default', {
|
|
instanceId,
|
|
memberId,
|
|
})
|
|
}
|
|
|
|
// Update a managed Modrinth instance to a specific version
|
|
export async function update_managed_modrinth_version(
|
|
instanceId: string,
|
|
versionId: string,
|
|
): Promise<InstallJobSnapshot> {
|
|
return await invoke('plugin:instance|instance_update_managed_modrinth_version', {
|
|
instanceId,
|
|
versionId,
|
|
})
|
|
}
|
|
|
|
// Repair a managed Modrinth instance
|
|
export async function update_repair_modrinth(instanceId: string): Promise<InstallJobSnapshot> {
|
|
return await invoke('plugin:instance|instance_repair_managed_modrinth', { instanceId })
|
|
}
|
|
|
|
// Export an instance to .mrpack
|
|
// included_overrides is an array of paths to override folders to include (ie: 'mods', 'resource_packs')
|
|
// Version id is optional (ie: 1.1.5)
|
|
export async function export_instance_mrpack(
|
|
instanceId: string,
|
|
exportLocation: string,
|
|
includedOverrides: string[],
|
|
versionId?: string,
|
|
description?: string,
|
|
name?: string,
|
|
): Promise<void> {
|
|
return await invoke('plugin:instance|instance_export_mrpack', {
|
|
instanceId,
|
|
exportLocation,
|
|
includedOverrides,
|
|
versionId,
|
|
description,
|
|
name,
|
|
})
|
|
}
|
|
|
|
// Given a folder path, populate an array of all the subfolders
|
|
// Intended to be used for finding potential override folders
|
|
// profile
|
|
// -- mods
|
|
// -- resourcepacks
|
|
// -- file1
|
|
// => [mods, resourcepacks]
|
|
// allows selection for 'included_overrides' in export_instance_mrpack
|
|
export async function get_pack_export_candidates(instanceId: string): Promise<string[]> {
|
|
return await invoke('plugin:instance|instance_get_pack_export_candidates', { instanceId })
|
|
}
|
|
|
|
export interface GcLaunchIntent {
|
|
active_preset_id: string
|
|
block_tokens: string[]
|
|
candidate_ids: string[]
|
|
candidates: string[][]
|
|
}
|
|
|
|
export interface GcLaunchReport {
|
|
preferred_strategy: string
|
|
chosen_strategy: string
|
|
chosen_args: string[]
|
|
pruned_args: string[]
|
|
reason_chain: string[]
|
|
}
|
|
|
|
export interface InstanceRunResult {
|
|
process: unknown
|
|
gc_notice: GcLaunchReport | null
|
|
}
|
|
|
|
export function gcReportFellBack(report: GcLaunchReport): boolean {
|
|
return report.chosen_strategy !== report.preferred_strategy || report.pruned_args.length > 0
|
|
}
|
|
|
|
/**
|
|
* The Java that will actually run the instance: a per-instance `java_path`
|
|
* wins, otherwise the optimal JRE for the game version.
|
|
*/
|
|
async function resolveEffectiveJava(
|
|
instance: Pick<GameInstance, 'java_path'>,
|
|
instanceId: string,
|
|
): Promise<JavaVersion | null> {
|
|
if (instance.java_path) {
|
|
try {
|
|
return await get_jre(instance.java_path)
|
|
} catch {
|
|
// Invalid override; fall back to the optimal JRE.
|
|
}
|
|
}
|
|
return await get_optimal_jre_key(instanceId)
|
|
}
|
|
|
|
function normalizeJvmToken(token: string): string {
|
|
const stripped = token.replace(/^-+/, '')
|
|
const withoutPrefix = stripped.startsWith('XX:') ? stripped.slice(3) : stripped
|
|
const withoutModifier =
|
|
withoutPrefix.startsWith('+') || withoutPrefix.startsWith('-')
|
|
? withoutPrefix.slice(1)
|
|
: withoutPrefix
|
|
return withoutModifier.split('=')[0] ?? withoutModifier
|
|
}
|
|
|
|
function strategyTokenKeys(strategy: ResolvedGcStrategyId, context: GcContext): Set<string> {
|
|
const tokens = GC_STRATEGY_DEFINITIONS[strategy].buildArgs(context).split(/\s+/).filter(Boolean)
|
|
return new Set(tokens.map(normalizeJvmToken))
|
|
}
|
|
|
|
/**
|
|
* Determine whether the effective Java args contain an auto GC marker or an
|
|
* active GC preset block, and if so build the ordered candidate chain for the
|
|
* backend to verify against the actual JVM. The args are returned untouched so
|
|
* the backend replaces exactly the preset block.
|
|
*/
|
|
export async function resolveGcLaunchIntent(
|
|
instanceId: string,
|
|
): Promise<{ args: string[]; gcIntent: GcLaunchIntent | null }> {
|
|
const instance = await get(instanceId)
|
|
if (!instance) return { args: [], gcIntent: null }
|
|
|
|
const settings = await getSettings()
|
|
const effectiveArgs = instance.extra_launch_args ?? settings.extra_launch_args
|
|
|
|
const java = await resolveEffectiveJava(instance, instanceId)
|
|
const javaMajorVersion = java?.parsed_version ?? null
|
|
let modCount = 0
|
|
try {
|
|
const snapshot = await get_content_snapshot(instanceId)
|
|
modCount = snapshot.items.filter(
|
|
(item) => item.projectType === 'mod' && item.materializationState === 'present',
|
|
).length
|
|
} catch {
|
|
modCount = 0
|
|
}
|
|
|
|
const memory = instance.memory ?? settings.memory
|
|
let allocatedMemoryMb = memory.maximum
|
|
if (memory.automatic) {
|
|
try {
|
|
const status = await get_memory_status(instanceId, memory.maximum, true)
|
|
allocatedMemoryMb = status.allocated_mb
|
|
} catch {
|
|
// Fall back to the configured value when automatic memory info is unavailable.
|
|
}
|
|
}
|
|
const context = await collectGcContext(
|
|
allocatedMemoryMb,
|
|
instance.loader,
|
|
javaMajorVersion,
|
|
modCount,
|
|
)
|
|
|
|
let activePresetId: string | null = null
|
|
let blockTokens: string[] = []
|
|
if (effectiveArgs.includes(AUTO_GC_PRESET_ARG)) {
|
|
activePresetId = 'gc-auto'
|
|
blockTokens = [AUTO_GC_PRESET_ARG]
|
|
} else {
|
|
const detected = detectGcStrategy(effectiveArgs.join(' '))
|
|
if (detected) {
|
|
activePresetId = `gc-${detected}`
|
|
const presetKeys = strategyTokenKeys(detected, context)
|
|
blockTokens = effectiveArgs.filter((arg) => presetKeys.has(normalizeJvmToken(arg)))
|
|
}
|
|
}
|
|
|
|
if (!activePresetId || !blockTokens.length) {
|
|
return { args: effectiveArgs, gcIntent: null }
|
|
}
|
|
|
|
const preferred: ResolvedGcStrategyId =
|
|
activePresetId === 'gc-auto'
|
|
? resolveAutoGcStrategy(context).resolvedStrategy
|
|
: (activePresetId.slice('gc-'.length) as ResolvedGcStrategyId)
|
|
|
|
const { ids, args: candidates } = buildGcCandidateChain(context, preferred)
|
|
|
|
return {
|
|
args: effectiveArgs,
|
|
gcIntent: {
|
|
active_preset_id: activePresetId,
|
|
block_tokens: blockTokens,
|
|
candidate_ids: ids,
|
|
candidates,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Run Minecraft using an instance
|
|
// Returns the process metadata plus an optional GC fallback report.
|
|
export async function run(
|
|
instanceId: string,
|
|
serverAddress: string | null = null,
|
|
): Promise<InstanceRunResult> {
|
|
if (await getInstanceMode(instanceId) === 'starlight') await prepareHostedSession()
|
|
const { args, gcIntent } = await resolveGcLaunchIntent(instanceId)
|
|
const result = await invoke<InstanceRunResult>('plugin:instance|instance_run', {
|
|
instanceId,
|
|
serverAddress,
|
|
offlineMode: isOfflineMode(),
|
|
extraLaunchArgs: args,
|
|
gcIntent,
|
|
})
|
|
setLastGcLaunchReport(result.gc_notice)
|
|
return result
|
|
}
|
|
|
|
export async function kill(instanceId: string): Promise<void> {
|
|
return await invoke('plugin:instance|instance_kill', { instanceId })
|
|
}
|
|
|
|
// Edits an instance
|
|
export async function edit(instanceId: string, editInstance: Partial<GameInstance>): Promise<void> {
|
|
return await invoke('plugin:instance|instance_edit', { instanceId, editInstance })
|
|
}
|
|
|
|
export async function cache_icon(iconName: string, bytes: number[]): Promise<string> {
|
|
return await invoke('plugin:instance|instance_cache_icon', { iconName, bytes })
|
|
}
|
|
|
|
// Edits an instance's icon
|
|
export async function edit_icon(instanceId: string, iconPath: string | null): Promise<void> {
|
|
return await invoke('plugin:instance|instance_edit_icon', { instanceId, iconPath })
|
|
}
|
|
|
|
export type SymlinkCapability = 'supported' | 'requires_admin' | 'unsupported'
|
|
|
|
export async function check_symlink_capability(): Promise<SymlinkCapability> {
|
|
return await invoke('check_symlink_capability')
|
|
}
|
|
|
|
export async function allow_symlink_target(path: string): Promise<void> {
|
|
return await invoke('allow_symlink_target', { path })
|
|
}
|