diff options
Diffstat (limited to 'docs/handbook/meshmc/instance-management.md')
| -rw-r--r-- | docs/handbook/meshmc/instance-management.md | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/docs/handbook/meshmc/instance-management.md b/docs/handbook/meshmc/instance-management.md new file mode 100644 index 0000000000..801050fa10 --- /dev/null +++ b/docs/handbook/meshmc/instance-management.md @@ -0,0 +1,483 @@ +# Instance Management + +## Overview + +Instance management is central to MeshMC's design. An "instance" is a self-contained Minecraft environment with its own game version, mods, settings, saves, resource packs, and configuration. MeshMC stores multiple instances in parallel, allowing users to maintain entirely separate Minecraft setups. + +## Instance Storage Layout + +### Instance Root Directory + +All instances live under a single parent directory, configurable in settings (default: `instances/` within the MeshMC data directory). Each instance occupies its own subdirectory: + +``` +instances/ +├── MyVanilla1.20/ +│ ├── instance.cfg # Instance-level settings (INI format) +│ ├── mmc-pack.json # Component list (PackProfile) +│ ├── patches/ # Custom component overrides (JSON) +│ ├── .minecraft/ # Game directory +│ │ ├── mods/ # Loader mods +│ │ ├── resourcepacks/ # Resource packs +│ │ ├── shaderpacks/ # Shader packs +│ │ ├── saves/ # World saves +│ │ ├── config/ # Mod configuration +│ │ ├── options.txt # Game options +│ │ ├── screenshots/ # Screenshots +│ │ └── logs/ # Game logs +│ └── libraries/ # Instance-local libraries +├── ForgeModded/ +│ ├── instance.cfg +│ ├── mmc-pack.json +│ ├── patches/ +│ └── .minecraft/ +└── ... +``` + +### Instance Configuration File (`instance.cfg`) + +Each instance has an `instance.cfg` file (INI format) managed by `INISettingsObject`. This stores per-instance metadata and setting overrides: + +```ini +InstanceType=OneSix +name=My Modded Instance +iconKey=flame +notes=Testing Forge 1.20.4 with performance mods +lastLaunchTime=1712345678000 +totalTimePlayed=3600 +lastTimePlayed=1800 +JoinServerOnLaunch=false +OverrideJavaPath=true +JavaPath=/usr/lib/jvm/java-21/bin/java +OverrideMemory=true +MinMemAlloc=2048 +MaxMemAlloc=8192 +``` + +## BaseInstance Class + +`BaseInstance` is the abstract base class for all instance types, defined in `launcher/BaseInstance.h`: + +```cpp +class BaseInstance : public QObject, + public std::enable_shared_from_this<BaseInstance> +{ + Q_OBJECT +protected: + BaseInstance(SettingsObjectPtr globalSettings, + SettingsObjectPtr settings, + const QString& rootDir); +public: + enum class Status { Present, Gone }; + + virtual void saveNow() = 0; + void invalidate(); + + virtual QString id() const; + void setRunning(bool running); + bool isRunning() const; + int64_t totalTimePlayed() const; + int64_t lastTimePlayed() const; + void resetTimePlayed(); + + QString instanceType() const; + QString instanceRoot() const; + virtual QString gameRoot() const { return instanceRoot(); } + virtual QString modsRoot() const = 0; + + QString name() const; + void setName(QString val); + QString windowTitle() const; + QString iconKey() const; + void setIconKey(QString val); + QString notes() const; + void setNotes(QString val); + + QString getPreLaunchCommand(); + QString getPostExitCommand(); + QString getWrapperCommand(); + + virtual QSet<QString> traits() const = 0; + qint64 lastLaunch() const; + void setLastLaunch(qint64 val); + + virtual SettingsObjectPtr settings() const; + virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0; + virtual shared_qobject_ptr<LaunchTask> + createLaunchTask(AuthSessionPtr account, + MinecraftServerTargetPtr serverToJoin) = 0; + shared_qobject_ptr<LaunchTask> getLaunchTask(); + virtual QProcessEnvironment createEnvironment() = 0; + virtual IPathMatcher::Ptr getLogFileMatcher() = 0; + virtual QString getLogFileRoot() = 0; +}; + +typedef std::shared_ptr<BaseInstance> InstancePtr; +``` + +Key characteristics: +- Uses `std::enable_shared_from_this` for safe self-reference in callbacks +- Instance ID is determined internally by MeshMC (typically the directory name) +- Tracks play time (total and last session) in milliseconds +- Supports custom pre-launch, post-exit, and wrapper commands +- `traits()` returns feature flags from the version profile (e.g., `"XR:Initial"`, `"FirstThreadOnMacOS"`) + +## MinecraftInstance + +`MinecraftInstance` is the concrete implementation of `BaseInstance` for modern Minecraft versions, defined in `launcher/minecraft/MinecraftInstance.h`: + +```cpp +class MinecraftInstance : public BaseInstance +{ + Q_OBJECT +public: + MinecraftInstance(SettingsObjectPtr globalSettings, + SettingsObjectPtr settings, + const QString& rootDir); + + // Directory accessors + QString jarModsDir() const; + QString resourcePacksDir() const; + QString texturePacksDir() const; + QString shaderPacksDir() const; + QString modsRoot() const override; + QString coreModsDir() const; + QString modsCacheLocation() const; + QString libDir() const; + QString worldDir() const; + QString resourcesDir() const; + QDir jarmodsPath() const; + QDir librariesPath() const; + QDir versionsPath() const; + QString instanceConfigFolder() const override; + QString gameRoot() const override; + QString binRoot() const; + QString getNativePath() const; + QString getLocalLibraryPath() const; + + // Component system + std::shared_ptr<PackProfile> getPackProfile() const; + + // Mod folder models + std::shared_ptr<ModFolderModel> loaderModList() const; + std::shared_ptr<ModFolderModel> coreModList() const; + std::shared_ptr<ModFolderModel> resourcePackList() const; + std::shared_ptr<ModFolderModel> texturePackList() const; + std::shared_ptr<ModFolderModel> shaderPackList() const; + std::shared_ptr<WorldList> worldList() const; + std::shared_ptr<GameOptions> gameOptionsModel() const; + + // Launch + Task::Ptr createUpdateTask(Net::Mode mode) override; + shared_qobject_ptr<LaunchTask> + createLaunchTask(AuthSessionPtr account, + MinecraftServerTargetPtr serverToJoin) override; + QStringList javaArguments() const; + QStringList getClassPath() const; + QStringList getNativeJars() const; + QString getMainClass() const; + QStringList processMinecraftArgs(AuthSessionPtr account, + MinecraftServerTargetPtr serverToJoin) const; + JavaVersion getJavaVersion() const; +}; +``` + +`MinecraftInstance` provides: +- All directory path resolution for game assets +- Lazy-initialized folder models (`ModFolderModel`, `WorldList`, etc.) +- Launch task construction with the full step pipeline +- Java argument assembly including classpath, library paths, and game arguments +- Version-specific behaviors via traits + +## InstanceList + +`InstanceList` manages the collection of all instances, defined in `launcher/InstanceList.h`: + +```cpp +class InstanceList : public QAbstractListModel +{ + Q_OBJECT +public: + explicit InstanceList(SettingsObjectPtr settings, + const QString& instDir, QObject* parent = 0); + + // Model interface + QModelIndex index(int row, int column = 0, + const QModelIndex& parent = QModelIndex()) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role) const override; + Qt::ItemFlags flags(const QModelIndex& index) const override; + bool setData(const QModelIndex& index, const QVariant& value, + int role) override; + + enum AdditionalRoles { + GroupRole = Qt::UserRole, + InstancePointerRole = 0x34B1CB48, + InstanceIDRole = 0x34B1CB49 + }; + + InstancePtr at(int i) const { return m_instances.at(i); } +}; +``` + +### Instance Discovery + +On startup, `InstanceList` scans the instances directory: +1. Enumerates subdirectories in the instances folder +2. For each directory, looks for `instance.cfg` +3. Reads `InstanceType` from the config to determine the instance class +4. Creates the appropriate instance object (`MinecraftInstance`) +5. Adds it to the internal list and emits model change signals + +### Custom Model Roles + +| Role | Value | Returns | +|---|---|---| +| `GroupRole` | `Qt::UserRole` | Group name (QString) | +| `InstancePointerRole` | `0x34B1CB48` | `InstancePtr` (shared pointer) | +| `InstanceIDRole` | `0x34B1CB49` | Instance ID (QString) | + +## Instance Groups + +Instances can be organized into named groups. Group assignments are stored in `instgroups.json` in the data directory: + +```json +{ + "formatVersion": 1, + "groups": { + "Modded": { + "hidden": false, + "instances": [ + "ForgeModded", + "FabricServer" + ] + }, + "Vanilla": { + "hidden": false, + "instances": [ + "MyVanilla1.20" + ] + } + } +} +``` + +Group state tracking uses a tri-state enum: + +```cpp +enum class GroupsState { NotLoaded, Steady, Dirty }; +``` + +Groups are loaded lazily and saved when dirty. The `GroupRole` in the model provides group information for UI display. + +## Instance Creation + +### New Instance Creation + +New instances are created via `NewInstanceDialog`, which collects: +- Instance name and icon +- Minecraft version selection +- Optional mod loader (Forge, Fabric, Quilt, NeoForge) +- Optional modpack import + +The actual creation is handled by `InstanceCreationTask`: +1. Creates the instance directory +2. Writes initial `instance.cfg` +3. Creates a `PackProfile` with the selected components +4. Saves the component list to `mmc-pack.json` +5. Runs `ComponentUpdateTask` to resolve dependencies and download metadata + +### Instance Import + +`InstanceImportTask` handles importing instances from external sources: + +```cpp +class InstanceImportTask : public InstanceTask +{ + Q_OBJECT +public: + explicit InstanceImportTask(const QUrl& url); +protected: + virtual void executeTask() override; +}; +``` + +Supported import formats: +- **ZIP archives** — exported MeshMC/MultiMC instance packages +- **URLs** — downloads and extracts (supports CurseForge, Modrinth, and Technic pack URLs) +- **Modpack manifests** — platform-specific manifests trigger specialized import logic + +### Instance Copying + +`InstanceCopyTask` clones an existing instance: + +```cpp +class InstanceCopyTask : public InstanceTask +{ + Q_OBJECT +public: + explicit InstanceCopyTask(InstancePtr origInstance, + bool copySaves, bool keepPlaytime); +protected: + virtual void executeTask() override; +private: + InstancePtr m_origInstance; + QFuture<bool> m_copyFuture; + QFutureWatcher<bool> m_copyFutureWatcher; + std::unique_ptr<IPathMatcher> m_matcher; + bool m_keepPlaytime; +}; +``` + +Key options: +- `copySaves` — whether to include world save data +- `keepPlaytime` — whether to copy playtime statistics +- Uses `QtConcurrent` for background file copying with progress tracking +- An `IPathMatcher` can exclude specific files/directories from the copy + +The copy dialog is `CopyInstanceDialog`: + +```cpp +class CopyInstanceDialog : public QDialog +// UI file: CopyInstanceDialog.ui +``` + +## Instance Lifecycle States + +An instance progresses through several states during its lifetime: + +``` +Created → Present → [Running] → Present → [Gone] +``` + +The `BaseInstance::Status` enum tracks the primary state: + +```cpp +enum class Status { + Present, // Instance exists and is tracked + Gone // Instance was removed or invalidated +}; +``` + +Running state is tracked separately: + +```cpp +void BaseInstance::setRunning(bool running); +bool BaseInstance::isRunning() const; +``` + +When running: +- Play time counters are updated +- The instance icon shows a running indicator in the UI +- Certain operations are disabled (delete, move, etc.) + +## Instance Invalidation + +```cpp +void BaseInstance::invalidate(); +``` + +An instance is invalidated when: +- Its directory is externally deleted or moved +- A `RecursiveFileSystemWatcher` detects the directory change +- The `InstanceList` removes it from its model + +## Instance Settings Override System + +Each instance has its own `SettingsObject` that can override global settings: + +```cpp +virtual SettingsObjectPtr settings() const; +``` + +The override mechanism uses `OverrideSetting`: +- A **gate setting** (boolean) controls whether the override is active +- When the gate is ON, the instance's local value is used +- When the gate is OFF, the global value is passed through + +Common overridable settings: + +| Setting | Gate Setting | Purpose | +|---|---|---| +| `JavaPath` | `OverrideJavaPath` | Java binary path | +| `MinMemAlloc` | `OverrideMemory` | Minimum memory (MB) | +| `MaxMemAlloc` | `OverrideMemory` | Maximum memory (MB) | +| `JvmArgs` | `OverrideJavaArgs` | Additional JVM arguments | +| `MCLaunchMethod` | `OverrideMCLaunchMethod` | Launch method | +| `PreLaunchCommand` | `OverrideCommands` | Pre-launch command | +| `PostExitCommand` | `OverrideCommands` | Post-exit command | +| `WrapperCommand` | `OverrideCommands` | Wrapper command | +| `WindowWidth` | `OverrideWindow` | Window width | +| `WindowHeight` | `OverrideWindow` | Window height | +| `MaximizeWindow` | `OverrideWindow` | Start maximized | + +The `InstanceSettingsPage` UI provides checkboxes for each gate setting, enabling or disabling the corresponding override section. + +## Instance UI Integration + +### Instance View + +The main window displays instances in a custom view (`InstanceView` in `ui/instanceview/`): +- Grid or list layout +- Group headers with collapse/expand +- Drag and drop between groups +- Context menu for instance operations +- Icon display with status overlay (running indicator) + +### Instance Pages + +When editing an instance, a `PageDialog` opens with these pages: + +| Page | File | Purpose | +|---|---|---| +| `VersionPage` | `ui/pages/instance/VersionPage.{h,cpp}` | Component management | +| `ModFolderPage` | `ui/pages/instance/ModFolderPage.{h,cpp}` | Mod list | +| `ResourcePackPage` | `ui/pages/instance/ResourcePackPage.h` | Resource packs | +| `TexturePackPage` | `ui/pages/instance/TexturePackPage.h` | Texture packs | +| `ShaderPackPage` | `ui/pages/instance/ShaderPackPage.h` | Shader packs | +| `NotesPage` | `ui/pages/instance/NotesPage.{h,cpp}` | Instance notes | +| `LogPage` | `ui/pages/instance/LogPage.{h,cpp}` | Game log viewer | +| `ScreenshotsPage` | `ui/pages/instance/ScreenshotsPage.{h,cpp}` | Screenshots | +| `WorldListPage` | `ui/pages/instance/WorldListPage.{h,cpp}` | World management | +| `GameOptionsPage` | `ui/pages/instance/GameOptionsPage.{h,cpp}` | Game options editor | +| `ServersPage` | `ui/pages/instance/ServersPage.{h,cpp}` | Server list editor | +| `InstanceSettingsPage` | `ui/pages/instance/InstanceSettingsPage.{h,cpp}` | Settings overrides | +| `OtherLogsPage` | `ui/pages/instance/OtherLogsPage.{h,cpp}` | Additional log files | + +### Instance Window + +`InstanceWindow` provides a dedicated window for a running instance: + +```cpp +class InstanceWindow : public QMainWindow +{ + Q_OBJECT +}; +``` + +It displays: +- Real-time game log output via `LogModel` +- Launch/kill controls +- Instance page navigation + +## Instance Export + +`ExportInstanceDialog` allows exporting an instance to a ZIP archive: +- Select which files/directories to include +- Exclude sensitive data, caches, and temporary files +- The exported archive can be imported by MeshMC on another machine + +## Play Time Tracking + +Each instance tracks cumulative play time: + +```cpp +int64_t BaseInstance::totalTimePlayed() const; +int64_t BaseInstance::lastTimePlayed() const; +void BaseInstance::resetTimePlayed(); +``` + +- `totalTimePlayed` — cumulative milliseconds across all sessions +- `lastTimePlayed` — duration of the most recent session +- Time is recorded when `setRunning(false)` is called after a session +- Stored in `instance.cfg` as `totalTimePlayed` and `lastTimePlayed` |
