Class ServerModsViewModel
- Namespace
- McServerLauncher.ViewModels
- Assembly
- McServerLauncher.dll
The "you can clean up" panel: libraries nothing uses any more, and mods switched off for a month.
public sealed class ServerModsViewModel : ObservableObject, INotifyPropertyChanged, INotifyPropertyChanging, IDisposable
- Inheritance
-
ServerModsViewModel
- Implements
- Inherited Members
Remarks
Only suggested, never done: the panel lists, the user ticks, the user presses the button. What is listed is decided by McServerLauncher.Services.CleanupAdvisor; this half finds the facts it needs (the jars, what the store knows about them, the ledger) and puts its answer on screen.
Nothing here runs until the tab has been shown. The panel is also built for servers whose tab is never opened, and by tests that have no business writing to the user's application data or asking Modrinth anything; EnsureLoaded() is what arms it.
Constructors
ServerModsViewModel(ServerConfig)
public ServerModsViewModel(ServerConfig config)
Parameters
configServerConfig
Properties
ActiveFilters
Everything narrowing the results right now, as one row of chips: the server's own loader and Minecraft version (always on, not removable) followed by the categories the user picked. Putting them together answers "why am I seeing these results?" in one glance.
public ObservableCollection<ActiveFilterViewModel> ActiveFilters { get; }
Property Value
BrowseTags
The category chips, built from the tag catalogue and filtered to what makes sense for this kind of server. Tags that map to a Modrinth facet filter exactly; the rest fall back to a search term.
public IReadOnlyList<StoreTagViewModel> BrowseTags { get; }
Property Value
Remarks
Rebuilt, not fixed at construction: converting a server between the two families changes which catalogue it browses, and a Paper server offering "Adventure & RPG" because it used to be Fabric would search for something Modrinth has no plugins under.
BrowseTitle
public string BrowseTitle { get; }
Property Value
CanIncludeExcludedAnyway
True while there is something to put back, which is what the button hangs off.
public bool CanIncludeExcludedAnyway { get; }
Property Value
CanLoadMore
[ExcludeFromCodeCoverage]
public bool CanLoadMore { get; set; }
Property Value
CanRemoveSelectedCleanup
public bool CanRemoveSelectedCleanup { get; }
Property Value
Remarks
Not while "update all" runs: it walks the installed rows and swaps their files, and removing rebuilds those rows under it.
CheckUpdatesCommand
Gets an IAsyncRelayCommand instance wrapping CheckUpdates(CancellationToken).
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand CheckUpdatesCommand { get; }
Property Value
CleanupSuggestions
What the panel lists right now.
public ObservableCollection<CleanupSuggestionViewModel> CleanupSuggestions { get; }
Property Value
ClearFiltersCommand
Gets an IRelayCommand instance wrapping McServerLauncher.ViewModels.ServerModsViewModel.ClearFilters().
[ExcludeFromCodeCoverage]
public IRelayCommand ClearFiltersCommand { get; }
Property Value
ClientPackMods
The client-only mods added for this server's players: Sodium, MaLiLib, a minimap. The server never loads them (see ClientPack); the export puts them in the pack.
public ObservableCollection<ModItem> ClientPackMods { get; }
Property Value
ClientPackTitleText
"Players' pack (3)".
public string ClientPackTitleText { get; }
Property Value
ContentTabTitle
public string ContentTabTitle { get; }
Property Value
DeleteModCommand
Gets an IAsyncRelayCommand<T> instance wrapping DeleteMod(ModItem).
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand<ModItem?> DeleteModCommand { get; }
Property Value
Details
The open details page, or null while the browser list is showing. The list is deliberately left untouched underneath, so coming back is instant and keeps the scroll position.
[ExcludeFromCodeCoverage]
public ModDetailsViewModel? Details { get; set; }
Property Value
DismissCleanupCommand
Gets an IRelayCommand instance wrapping McServerLauncher.ViewModels.ServerModsViewModel.DismissCleanup().
[ExcludeFromCodeCoverage]
public IRelayCommand DismissCleanupCommand { get; }
Property Value
DismissExportNoticeCommand
Gets an IRelayCommand instance wrapping McServerLauncher.ViewModels.ServerModsViewModel.DismissExportNotice().
[ExcludeFromCodeCoverage]
public IRelayCommand DismissExportNoticeCommand { get; }
Property Value
DismissInstallNoticeCommand
Gets an IRelayCommand instance wrapping McServerLauncher.ViewModels.ServerModsViewModel.DismissInstallNotice().
[ExcludeFromCodeCoverage]
public IRelayCommand DismissInstallNoticeCommand { get; }
Property Value
ExportModpackCommand
Gets an IAsyncRelayCommand instance wrapping McServerLauncher.ViewModels.ServerModsViewModel.ExportModpack().
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand ExportModpackCommand { get; }
Property Value
ExportNotice
What the last export did, shown under the installed list until dismissed.
[ExcludeFromCodeCoverage]
public string? ExportNotice { get; set; }
Property Value
Remarks
Deliberately after the fact and not a dialog beforehand. A dialog charges a click to the normal case — the one where the detection is right — in order to serve the rare one, and per-mod checkboxes are precision nobody needs when including one jar too many costs a few megabytes. Naming the excluded files is what makes this actionable instead of unsettling.
FacetTags
Categories Modrinth can filter on exactly. These stack: each adds its own facet group, and Modrinth ANDs the groups.
public IReadOnlyList<StoreTagViewModel> FacetTags { get; }
Property Value
FilterTip
public string FilterTip { get; }
Property Value
FilterTypeBrush
public IBrush FilterTypeBrush { get; }
Property Value
- IBrush
FilterTypeText
public string FilterTypeText { get; }
Property Value
FilterVersionText
public string FilterVersionText { get; }
Property Value
HasCleanupSuggestions
public bool HasCleanupSuggestions { get; }
Property Value
HasClientPack
public bool HasClientPack { get; }
Property Value
HasExportNotice
public bool HasExportNotice { get; }
Property Value
HasInstallNotice
public bool HasInstallNotice { get; }
Property Value
HasMissingDependencies
public bool HasMissingDependencies { get; }
Property Value
HasNoMatches
Something is installed, but the search leaves none of it.
public bool HasNoMatches { get; }
Property Value
HasPendingUpdates
public bool HasPendingUpdates { get; }
Property Value
HasSelectedTags
public bool HasSelectedTags { get; }
Property Value
HowToPlaySteps
Instructions to play: install the loader client (mods) or just join (plugins).
public string HowToPlaySteps { get; }
Property Value
HowToPlayTitle
public string HowToPlayTitle { get; }
Property Value
IncludeExcludedAnywayCommand
Gets an IAsyncRelayCommand instance wrapping McServerLauncher.ViewModels.ServerModsViewModel.IncludeExcludedAnyway().
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand IncludeExcludedAnywayCommand { get; }
Property Value
InstallFailed
True when the notice reports a failure, which draws it in red rather than green.
[ExcludeFromCodeCoverage]
public bool InstallFailed { get; set; }
Property Value
InstallMissingDependenciesCommand
Gets an IAsyncRelayCommand instance wrapping InstallMissingDependencies(CancellationToken).
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand InstallMissingDependenciesCommand { get; }
Property Value
InstallNotice
What the last install said, shown as a banner over the store and the details page.
[ExcludeFromCodeCoverage]
public string? InstallNotice { get; set; }
Property Value
InstalledCountText
"12", or "3 of 12" while searching.
public string InstalledCountText { get; }
Property Value
InstalledFilter
The words typed in the installed list's search box.
[ExcludeFromCodeCoverage]
public string InstalledFilter { get; set; }
Property Value
InstalledFilterPlaceholder
public string InstalledFilterPlaceholder { get; }
Property Value
InstalledMods
public ObservableCollection<ModItem> InstalledMods { get; }
Property Value
InstalledTitle
public string InstalledTitle { get; }
Property Value
IsBrowsing
public bool IsBrowsing { get; }
Property Value
IsCheckingUpdates
[ExcludeFromCodeCoverage]
public bool IsCheckingUpdates { get; set; }
Property Value
IsFiltersOpen
Whether the filters popup is open (its toggle button binds both ways).
[ExcludeFromCodeCoverage]
public bool IsFiltersOpen { get; set; }
Property Value
IsHowToPlayOpen
Whether the "how to play" popup is open.
[ExcludeFromCodeCoverage]
public bool IsHowToPlayOpen { get; set; }
Property Value
IsInstalledFiltered
public bool IsInstalledFiltered { get; }
Property Value
IsInstalledPanelOpen
Whether the installed panel is showing. Collapsing it is the cheapest way to give the results room: it is 300px that mostly holds a short list, and on a 1150px window it is the difference between one column of results and two.
[ExcludeFromCodeCoverage]
public bool IsInstalledPanelOpen { get; set; }
Property Value
IsInstallingDependencies
[ExcludeFromCodeCoverage]
public bool IsInstallingDependencies { get; set; }
Property Value
IsPluginBased
public bool IsPluginBased { get; }
Property Value
IsSearching
[ExcludeFromCodeCoverage]
public bool IsSearching { get; set; }
Property Value
IsUpdatingAll
[ExcludeFromCodeCoverage]
public bool IsUpdatingAll { get; set; }
Property Value
LoadMoreCommand
Gets an IAsyncRelayCommand instance wrapping LoadMore(CancellationToken).
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand LoadMoreCommand { get; }
Property Value
MissingDependencyText
[ExcludeFromCodeCoverage]
public string? MissingDependencyText { get; set; }
Property Value
NoInstalledText
public string NoInstalledText { get; }
Property Value
PendingUpdateCount
How many rows are offering an update right now.
public int PendingUpdateCount { get; }
Property Value
QueryTags
Categories Modrinth has no facet for, which can only contribute words to the query. Picking a second one would blur the search instead of narrowing it, so they behave as a radio group.
public IReadOnlyList<StoreTagViewModel> QueryTags { get; }
Property Value
RefreshInstalledModsCommand
Gets an IRelayCommand instance wrapping McServerLauncher.ViewModels.ServerModsViewModel.RefreshInstalledMods().
[ExcludeFromCodeCoverage]
public IRelayCommand RefreshInstalledModsCommand { get; }
Property Value
RemoveFromClientPackCommand
Gets an IRelayCommand<T> instance wrapping RemoveFromClientPack(ModItem).
[ExcludeFromCodeCoverage]
public IRelayCommand<ModItem?> RemoveFromClientPackCommand { get; }
Property Value
RemoveSelectedCleanupCommand
Gets an IRelayCommand instance wrapping McServerLauncher.ViewModels.ServerModsViewModel.RemoveSelectedCleanup().
[ExcludeFromCodeCoverage]
public IRelayCommand RemoveSelectedCleanupCommand { get; }
Property Value
RemoveSelectedText
"Quitar 1 seleccionado" / "Quitar 3 seleccionados".
public string RemoveSelectedText { get; }
Property Value
RemoveTagCommand
Gets an IRelayCommand<T> instance wrapping RemoveTag(StoreTagViewModel).
[ExcludeFromCodeCoverage]
public IRelayCommand<StoreTagViewModel?> RemoveTagCommand { get; }
Property Value
SearchCommand
Gets an IAsyncRelayCommand instance wrapping Search(CancellationToken).
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand SearchCommand { get; }
Property Value
SearchPlaceholder
public string SearchPlaceholder { get; }
Property Value
SearchQuery
[ExcludeFromCodeCoverage]
public string SearchQuery { get; set; }
Property Value
SearchResults
The results, flat. Replaced in one go rather than emptied and refilled: a list that sits empty across a network round trip makes the whole panel blink out and back.
public BulkObservableCollection<ModrinthProjectViewModel> SearchResults { get; }
Property Value
SearchStatus
[ExcludeFromCodeCoverage]
public string SearchStatus { get; set; }
Property Value
SelectedCleanupCount
How many rows are ticked.
public int SelectedCleanupCount { get; }
Property Value
SelectedSideIndex
Index into SideOptions. Everything by default: nothing is hidden.
[ExcludeFromCodeCoverage]
public int SelectedSideIndex { get; set; }
Property Value
SelectedSortIndex
[ExcludeFromCodeCoverage]
public int SelectedSortIndex { get; set; }
Property Value
SelectedTags
The categories currently filtering the browse, in the order the user picked them. Shown as removable chips, so a filter can never be selected but scrolled out of sight.
public ObservableCollection<StoreTagViewModel> SelectedTags { get; }
Property Value
ShowsSideFilter
Plugins have no client side, so the choice does not exist on a Paper server and the control is not drawn.
public bool ShowsSideFilter { get; }
Property Value
SideOptions
The three ways to cut the browser by where a project runs: everything, what can run on the server, and the client-only mods that go in the players' pack.
public IReadOnlyList<string> SideOptions { get; }
Property Value
SortOptions
Sort options shown in the UI. Index 0 = relevance, 1 = downloads.
public IReadOnlyList<string> SortOptions { get; }
Property Value
ToggleModCommand
Gets an IRelayCommand<T> instance wrapping ToggleMod(ModItem).
[ExcludeFromCodeCoverage]
public IRelayCommand<ModItem?> ToggleModCommand { get; }
Property Value
ToggleTagCommand
Gets an IRelayCommand<T> instance wrapping ToggleTag(StoreTagViewModel).
[ExcludeFromCodeCoverage]
public IRelayCommand<StoreTagViewModel?> ToggleTagCommand { get; }
Property Value
UpdateAllCommand
Gets an IAsyncRelayCommand instance wrapping McServerLauncher.ViewModels.ServerModsViewModel.UpdateAll().
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand UpdateAllCommand { get; }
Property Value
UpdateAllText
The label of the button that updates them all, with how many there are.
public string UpdateAllText { get; }
Property Value
UpdateModCommand
Gets an IAsyncRelayCommand<T> instance wrapping UpdateMod(ModItem).
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand<ModItem?> UpdateModCommand { get; }
Property Value
UpdateStatus
[ExcludeFromCodeCoverage]
public string UpdateStatus { get; set; }
Property Value
VisibleInstalledMods
What the installed list shows: InstalledMods through InstalledFilter.
public BulkObservableCollection<ModItem> VisibleInstalledMods { get; }
Property Value
Remarks
A copy rather than a filter on InstalledMods itself, because that one is also what counts pending updates and what "update all" walks. Filtering it in place would make a search box quietly hide updates from both.
Methods
Dispose()
Shuts down for good and lets go of the tokens, for a server that is being removed. Shutdown() is the one for an app that may carry on.
public void Dispose()
EnsureLoaded()
Loads the first page of mods the first time the Mods tab is shown.
public void EnsureLoaded()
GoBack()
Goes back to the previous details page, or to the list when there isn't one.
public void GoBack()
InstallMissingAsync(IEnumerable<string>, IProgress<string>?, CancellationToken)
Fetches the mods or plugins named by ids, for the check before a start.
public Task InstallMissingAsync(IEnumerable<string> ids, IProgress<string>? log = null, CancellationToken ct = default)
Parameters
idsIEnumerable<string>logIProgress<string>ctCancellationToken
Returns
Remarks
The ids come from the jars, and a mod id is not a store id: cristallib is published on
Modrinth as cristel-lib, and there is no rule that maps one to the other. So anything
the store does not recognise is skipped rather than reported as an error — the caller checks
again afterwards and says what is still missing, which is the honest answer.
Progress goes to the caller's log rather than to this tab's status line: the user pressed Start and is looking at the server's console, not at the Mods tab.
InstallModAsync(string, VersionResult?, bool)
Installs a project. chosen installs that exact version (the details page
offers the last few); without it the newest compatible one is resolved, as before.
This is the only install path in the app — the details page routes through it too.
public Task<bool> InstallModAsync(string projectId, VersionResult? chosen = null, bool toPlayerPack = false)
Parameters
projectIdstringThe project to install.
chosenVersionResultOne exact version, or null for the newest compatible one.
toPlayerPackboolPut it in the players' pack instead of the server's folder: for the client-only mods, which do nothing on a server. Everything else about the install is the same.
Returns
RefreshFromConfig()
Re-reads everything in this panel that comes from the server's type and Minecraft version.
public void RefreshFromConfig()
Remarks
The panel reads both straight off the ServerConfig it was handed, and that object is mutated in place when a server is converted or moved to another version. None of the properties derived from it announce anything on their own, so without this call the tab kept the old family's name, the old version's filter chip and the old folder's jars until the app was restarted — while every search it ran used the new values. The two disagreeing is what made an install fail later: the results on screen had been chosen for a version the server no longer runs.
Everything here is idempotent, so callers that are not sure whether anything moved can just call it.
ReloadInstalled()
Re-reads the content folder, for jars that arrived without the user putting them there.
public void ReloadInstalled()
Remarks
The panel takes its snapshot when it is built, which is the same moment the server is created — before crossplay has downloaded Geyser and Floodgate into it. Without this the two jars are on disk and loaded by the server while the Mods tab still shows nothing, and the only way to see them is the refresh button, which nobody thinks to press for mods they did not install.
ShowDetails(StoreItem)
Opens the details page for a store item, remembering the current one.
public void ShowDetails(StoreItem item)
Parameters
itemStoreItem
Shutdown()
Drops the open details page, cancelling whatever it is still fetching. Called when the app closes so pending requests don't outlive the window they were going to fill.
public void Shutdown()