Class ServerViewModel
- Namespace
- McServerLauncher.ViewModels
- Assembly
- McServerLauncher.dll
The crossplay half of ServerViewModel: Geyser, the Bedrock tunnel and the address.
public sealed class ServerViewModel : ObservableObject, INotifyPropertyChanged, INotifyPropertyChanging, IDisposable
- Inheritance
-
ServerViewModel
- Implements
- Inherited Members
Remarks
Split out because the file had reached two thousand lines carrying the process, the console, the players, the ports, the tunnels, crossplay, the backups, Java and the notifications. This is the part that has grown most and is likeliest to grow again, and it is a closed subject: everything here is about one question, and nothing outside it needs any of these members.
A move, not a rewrite. The class was already partial, so not one instruction changed — which is what makes it safe to do at all, and why the whole test suite must give the identical result.
Constructors
ServerViewModel(ServerConfig)
Assembles the view model. It does not start anything — see Activate().
public ServerViewModel(ServerConfig config)
Parameters
configServerConfig
Remarks
The split is the difference between a server that exists and one that is being watched. Everything here reads: the config, the console palette, the server's own files. Nothing here polls, opens a socket, subscribes to a process-wide singleton or goes to the network, so building one costs a few file reads and leaves nothing running behind it.
That is what makes the class reachable from a test at all. It is also the right shape on its own: a constructor that started three timers and two background requests had already begun doing its job before its caller had the chance to decide whether it wanted it to.
Properties
AddToWhitelistCommand
Gets an IAsyncRelayCommand instance wrapping McServerLauncher.ViewModels.ServerViewModel.AddToWhitelist().
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand AddToWhitelistCommand { get; }
Property Value
Backups
public ServerBackupsViewModel Backups { get; }
Property Value
BanPlayerCommand
Gets an IAsyncRelayCommand<T> instance wrapping BanPlayer(string).
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand<string?> BanPlayerCommand { get; }
Property Value
BannedPlayers
Banned players (banned-players.json).
public BulkObservableCollection<string> BannedPlayers { get; }
Property Value
BedrockHost
The Bedrock host and port, shown separately. Null when there is no Bedrock tunnel.
[ExcludeFromCodeCoverage]
public string? BedrockHost { get; set; }
Property Value
BedrockLocalPortText
The local UDP port Geyser listens on. Known from the moment crossplay is set up.
public string BedrockLocalPortText { get; }
Property Value
BedrockPortText
[ExcludeFromCodeCoverage]
public string? BedrockPortText { get; set; }
Property Value
BedrockPortsInUse
The Bedrock ports the other servers have already been given.
public Func<IEnumerable<int>>? BedrockPortsInUse { get; set; }
Property Value
Remarks
Supplied by MainViewModel, which is the only thing that knows the other servers exist.
It matters because the free-port search asks the operating system which UDP ports are
bound right now: set crossplay up on two stopped servers and both are handed 19132,
and the collision only shows up later as the second Geyser failing to bind.
BedrockState
How far along the Bedrock address is. Drives the explanation under the panel.
[ExcludeFromCodeCoverage]
public BedrockAddressState BedrockState { get; set; }
Property Value
BedrockStateText
One line saying what is happening, so the panel is never blank without a reason.
public string BedrockStateText { get; }
Property Value
CanStart
public bool CanStart { get; }
Property Value
CanStop
public bool CanStop { get; }
Property Value
ClearConsoleCommand
Gets an IRelayCommand instance wrapping McServerLauncher.ViewModels.ServerViewModel.ClearConsole().
[ExcludeFromCodeCoverage]
public IRelayCommand ClearConsoleCommand { get; }
Property Value
CommandHelp
Common commands with their explanation (for the console help).
public IReadOnlyList<CommandHelp> CommandHelp { get; }
Property Value
CommandText
[ExcludeFromCodeCoverage]
public string CommandText { get; set; }
Property Value
Config
public ServerConfig Config { get; }
Property Value
ConnectedPlayers
Players connected right now (live, read from the console).
public BulkObservableCollection<string> ConnectedPlayers { get; }
Property Value
ConnectionTest
The connection test on the network page, kept with its last result.
public ConnectionTestViewModel ConnectionTest { get; }
Property Value
ConsoleFilter
[ExcludeFromCodeCoverage]
public string ConsoleFilter { get; set; }
Property Value
ConsoleKinds
One switch per kind of line, in the order they appear along the top of the console.
public IReadOnlyList<ConsoleKindFilter> ConsoleKinds { get; }
Property Value
ConsoleLines
Lines of the embedded console (server stdout/stderr + the launcher's own).
public BulkObservableCollection<ConsoleLine> ConsoleLines { get; }
Property Value
CopyBedrockAddressCommand
Gets an IAsyncRelayCommand instance wrapping McServerLauncher.ViewModels.ServerViewModel.CopyBedrockAddress().
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand CopyBedrockAddressCommand { get; }
Property Value
CopyTunnelAddressCommand
Gets an IAsyncRelayCommand instance wrapping McServerLauncher.ViewModels.ServerViewModel.CopyTunnelAddress().
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand CopyTunnelAddressCommand { get; }
Property Value
CpuSeries
[ExcludeFromCodeCoverage]
public IReadOnlyList<double>? CpuSeries { get; set; }
Property Value
CpuText
[ExcludeFromCodeCoverage]
public string CpuText { get; set; }
Property Value
DeopPlayerCommand
Gets an IAsyncRelayCommand<T> instance wrapping DeopPlayer(string).
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand<string?> DeopPlayerCommand { get; }
Property Value
DismissPerformanceBandCommand
Gets an IRelayCommand instance wrapping McServerLauncher.ViewModels.ServerViewModel.DismissPerformanceBand().
[ExcludeFromCodeCoverage]
public IRelayCommand DismissPerformanceBandCommand { get; }
Property Value
GameVersionText
Minecraft version (empty until known).
public string GameVersionText { get; }
Property Value
HasBedrockAddress
True once there is a public Bedrock address worth showing.
public bool HasBedrockAddress { get; }
Property Value
HasIcon
public bool HasIcon { get; }
Property Value
HasIdleCountdown
True while there is a countdown to show.
public bool HasIdleCountdown { get; }
Property Value
HasPerformanceSubText
True when there is a line under the figure.
public bool HasPerformanceSubText { get; }
Property Value
HasRamDetail
True when there is a Java reading to show under the RAM figure.
public bool HasRamDetail { get; }
Property Value
History
Everyone who has been on this server, and each one's profile.
public PlayerHistoryViewModel History { get; }
Property Value
IdleCountdownText
How long until the empty server stops itself, or null when nothing is counting.
[ExcludeFromCodeCoverage]
public string? IdleCountdownText { get; set; }
Property Value
Remarks
Only set while the clock is actually running, so the UI can bind its visibility straight to this: an idle countdown showing "0:00" on a server nobody configured to stop would be a lie.
IsCommandHelpOpen
[ExcludeFromCodeCoverage]
public bool IsCommandHelpOpen { get; set; }
Property Value
IsConsoleFiltered
True while anything at all is being hidden, so the UI can offer to undo it.
public bool IsConsoleFiltered { get; }
Property Value
IsConverting
True while the settings page installs another loader into this server's folder.
[ExcludeFromCodeCoverage]
public bool IsConverting { get; set; }
Property Value
Remarks
Starting meanwhile would launch a jar that is being replaced. The button follows this, and so does every start without one — waking on demand, restarting after a crash.
IsCrossplayOn
Whether to show the Bedrock panel at all: the user asked for crossplay.
public bool IsCrossplayOn { get; }
Property Value
Remarks
Deliberately not "we successfully looked a tunnel up", which is what the panel used to be tied to. Everything the user needs in the first minute — the local port, and the fact that the app is still waiting on playit — is knowable before any lookup succeeds, and hiding the whole block until one did is what made a working server look like a broken one.
Announced through ServerConfigEffects when the config's own flag changes, which is why this panel no longer has a hand-written refresh of its own.
IsModded
public bool IsModded { get; }
Property Value
IsPreparing
True from the moment a start begins until the process is up or the start gives up.
[ExcludeFromCodeCoverage]
public bool IsPreparing { get; set; }
Property Value
Remarks
Before the process exists a start spends seconds — minutes, with a big world to back up — checking the port, the path, the dependencies and the Java, and making the backup, and all that time the state is still Stopped. Without this, anything that starts a server without the button (a second knock while waking on demand, an auto-restart, the button itself once it had finished its own run) began a second start beside the first: a second backup of the same world, and an error when it found the first one's process already there.
IsRestoring
True while a backup is being put back in place of the world.
public bool IsRestoring { get; }
Property Value
Remarks
Nothing may start the server meanwhile: it would open a world that is being swapped out from under it. The button follows this, and so does every start that has no button — waking on demand, restarting after a crash.
IsRunning
public bool IsRunning { get; }
Property Value
KeepAutomaticMemoryCommand
Gets an IRelayCommand instance wrapping McServerLauncher.ViewModels.ServerViewModel.KeepAutomaticMemory().
[ExcludeFromCodeCoverage]
public IRelayCommand KeepAutomaticMemoryCommand { get; }
Property Value
KickPlayerCommand
Gets an IAsyncRelayCommand<T> instance wrapping KickPlayer(string).
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand<string?> KickPlayerCommand { get; }
Property Value
KnownPlayers
Players who have ever joined (usercache.json).
public BulkObservableCollection<string> KnownPlayers { get; }
Property Value
MemoryNoticeManualText
The notice's way back, with the numbers it goes back to.
public string MemoryNoticeManualText { get; }
Property Value
MemoryPlan
What the current, or last, run started with. Null before the first start.
public MemoryPlan? MemoryPlan { get; }
Property Value
Mods
public ServerModsViewModel Mods { get; }
Property Value
MotdText
[ExcludeFromCodeCoverage]
public string MotdText { get; set; }
Property Value
Name
[ExcludeFromCodeCoverage]
public string Name { get; set; }
Property Value
NewWhitelistName
[ExcludeFromCodeCoverage]
public string NewWhitelistName { get; set; }
Property Value
OpPlayerCommand
Gets an IAsyncRelayCommand<T> instance wrapping OpPlayer(string).
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand<string?> OpPlayerCommand { get; }
Property Value
OpPlayers
Operators (ops.json).
public BulkObservableCollection<string> OpPlayers { get; }
Property Value
PardonPlayerCommand
Gets an IAsyncRelayCommand<T> instance wrapping PardonPlayer(string).
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand<string?> PardonPlayerCommand { get; }
Property Value
PerformanceBandIsSevere
The band's colours: amber when tight, red when saturated.
[ExcludeFromCodeCoverage]
public bool PerformanceBandIsSevere { get; set; }
Property Value
PerformanceBandText
The band's one line.
[ExcludeFromCodeCoverage]
public string PerformanceBandText { get; set; }
Property Value
PerformanceBrush
The colour of the figure: green, amber or red.
[ExcludeFromCodeCoverage]
public IBrush PerformanceBrush { get; set; }
Property Value
- IBrush
PerformanceSubText
The line under it.
[ExcludeFromCodeCoverage]
public string PerformanceSubText { get; set; }
Property Value
PerformanceText
The big figure on the Performance card: TPS when they can be measured, else a word.
[ExcludeFromCodeCoverage]
public string PerformanceText { get; set; }
Property Value
PerformanceTip
What the card's tooltip says.
[ExcludeFromCodeCoverage]
public string PerformanceTip { get; set; }
Property Value
PlayerCountText
[ExcludeFromCodeCoverage]
public string PlayerCountText { get; set; }
Property Value
PlayitState
[ExcludeFromCodeCoverage]
public PlayitState PlayitState { get; set; }
Property Value
PlayitStatusText
[ExcludeFromCodeCoverage]
public string PlayitStatusText { get; set; }
Property Value
PortText
[ExcludeFromCodeCoverage]
public string PortText { get; set; }
Property Value
RamDetailText
The line under the RAM figure: what Java has in use and what it holds, or empty.
[ExcludeFromCodeCoverage]
public string RamDetailText { get; set; }
Property Value
RamSeries
[ExcludeFromCodeCoverage]
public IReadOnlyList<double>? RamSeries { get; set; }
Property Value
RamText
[ExcludeFromCodeCoverage]
public string RamText { get; set; }
Property Value
RefreshPlayersCommand
Gets an IRelayCommand instance wrapping McServerLauncher.ViewModels.ServerViewModel.RefreshPlayers().
[ExcludeFromCodeCoverage]
public IRelayCommand RefreshPlayersCommand { get; }
Property Value
RemoveFromWhitelistCommand
Gets an IAsyncRelayCommand<T> instance wrapping RemoveFromWhitelist(string).
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand<string?> RemoveFromWhitelistCommand { get; }
Property Value
RestartCommand
Gets an IAsyncRelayCommand instance wrapping McServerLauncher.ViewModels.ServerViewModel.Restart().
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand RestartCommand { get; }
Property Value
ReturnToManualMemoryCommand
Gets an IRelayCommand instance wrapping McServerLauncher.ViewModels.ServerViewModel.ReturnToManualMemory().
[ExcludeFromCodeCoverage]
public IRelayCommand ReturnToManualMemoryCommand { get; }
Property Value
SendCommandCommand
Gets an IRelayCommand instance wrapping McServerLauncher.ViewModels.ServerViewModel.SendCommand().
[ExcludeFromCodeCoverage]
public IRelayCommand SendCommandCommand { get; }
Property Value
ServerIcon
[ExcludeFromCodeCoverage]
public Bitmap? ServerIcon { get; set; }
Property Value
- Bitmap
ServerTypeBrush
Badge color per type (shared palette; unknown/future types fall back to gray).
public IBrush ServerTypeBrush { get; }
Property Value
- IBrush
ServerTypeText
Server type shown as a badge (Vanilla/Fabric/Forge, and any future type).
public string ServerTypeText { get; }
Property Value
ShowAllConsoleKindsCommand
Gets an IRelayCommand instance wrapping McServerLauncher.ViewModels.ServerViewModel.ShowAllConsoleKinds().
[ExcludeFromCodeCoverage]
public IRelayCommand ShowAllConsoleKindsCommand { get; }
Property Value
ShowMemoryNotice
True while the server runs Automatic without its owner having chosen it.
public bool ShowMemoryNotice { get; }
Property Value
ShowPerformanceBand
True while a sustained problem is on and the owner has not closed the band.
[ExcludeFromCodeCoverage]
public bool ShowPerformanceBand { get; set; }
Property Value
ShowPerformanceSolutionsCommand
Gets an IRelayCommand instance wrapping McServerLauncher.ViewModels.ServerViewModel.ShowPerformanceSolutions().
[ExcludeFromCodeCoverage]
public IRelayCommand ShowPerformanceSolutionsCommand { get; }
Property Value
ShowTunnelWarning
[ExcludeFromCodeCoverage]
public bool ShowTunnelWarning { get; set; }
Property Value
ShowingSolutions
public bool ShowingSolutions { get; }
Property Value
ShowingTabs
public bool ShowingTabs { get; }
Property Value
SignalBrush
[ExcludeFromCodeCoverage]
public IBrush SignalBrush { get; set; }
Property Value
- IBrush
SignalHint
[ExcludeFromCodeCoverage]
public string SignalHint { get; set; }
Property Value
Solutions
The page that replaces the tabs, or null while the tabs show.
[ExcludeFromCodeCoverage]
public PerformanceSolutionsViewModel? Solutions { get; set; }
Property Value
StartCommand
Gets an IAsyncRelayCommand instance wrapping McServerLauncher.ViewModels.ServerViewModel.Start().
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand StartCommand { get; }
Property Value
State
[ExcludeFromCodeCoverage]
public ServerState State { get; set; }
Property Value
StatusBrush
[ExcludeFromCodeCoverage]
public IBrush StatusBrush { get; set; }
Property Value
- IBrush
StatusText
[ExcludeFromCodeCoverage]
public string StatusText { get; set; }
Property Value
StopCommand
Gets an IAsyncRelayCommand instance wrapping McServerLauncher.ViewModels.ServerViewModel.Stop().
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand StopCommand { get; }
Property Value
ToggleWhitelistCommand
Gets an IAsyncRelayCommand instance wrapping McServerLauncher.ViewModels.ServerViewModel.ToggleWhitelist().
[ExcludeFromCodeCoverage]
public IAsyncRelayCommand ToggleWhitelistCommand { get; }
Property Value
TunnelAddress
[ExcludeFromCodeCoverage]
public string? TunnelAddress { get; set; }
Property Value
TunnelState
How far along the public address is. Drives the line under the Playit row.
[ExcludeFromCodeCoverage]
public TunnelAddressState TunnelState { get; set; }
Property Value
TunnelStateText
One line saying what is happening, so the box is never blank without a reason.
public string TunnelStateText { get; }
Property Value
UptimeText
[ExcludeFromCodeCoverage]
public string UptimeText { get; set; }
Property Value
UseCommandCommand
Gets an IRelayCommand<T> instance wrapping UseCommand(CommandHelp).
[ExcludeFromCodeCoverage]
public IRelayCommand<CommandHelp?> UseCommandCommand { get; }
Property Value
VisibleConsoleLines
The lines currently shown in the console UI: all of ConsoleLines when nothing is filtered, or the matching subset (kept in order, updated incrementally as new lines arrive) while a search is typed or a category is switched off.
public BulkObservableCollection<ConsoleLine> VisibleConsoleLines { get; }
Property Value
WhitelistEnabled
[ExcludeFromCodeCoverage]
public bool WhitelistEnabled { get; set; }
Property Value
WhitelistPlayers
Players currently in the whitelist (names).
public BulkObservableCollection<string> WhitelistPlayers { get; }
Property Value
Methods
Activate()
Starts watching: the polling timer, the shared Playit state, the tunnel addresses and the wake-on-demand listener. ShutdownAsync() is its mirror.
public void Activate()
Remarks
Called by MainViewModel once the window is up, rather than by the constructor, so
that building a server and switching it on are two decisions instead of one. Everything in
here reaches outside the object — a shared singleton, a socket, the network — and none of it
is wanted by code that only needs to look at a server.
CreateBedrockTunnelAsync(string)
Creates the Bedrock tunnel of a crossplay server that has none (the tunnels screen offers it).
public Task CreateBedrockTunnelAsync(string playitKey)
Parameters
playitKeystring
Returns
Remarks
The port is on record by definition — it comes from this server's own config — so a tunnel already sitting on it is taken to be ours, which is what the setup flow assumes too.
CreateTunnelAsync(string)
Creates (if it doesn't exist) the Playit tunnel for this server's port, using the write key. Messages are shown in the server's console.
public Task CreateTunnelAsync(string writeKey)
Parameters
writeKeystring
Returns
Dispose()
Lets go of what this server owns, once it is off the list for good.
public void Dispose()
Remarks
After ShutdownAsync(), and only for a server that is being forgotten: shutting down is not the end (a failed update brings the same view models back), so it keeps everything usable and this is the one that does not.
LogLauncher(string)
Writes a line the launcher itself says into this server's console.
public void LogLauncher(string line)
Parameters
linestring
Remarks
For the tunnels screen, which acts on a server that is not the one on screen: its messages belong in that server's console, not in a dialog nobody asked for.
MoveBedrockPortAsync(string?)
Moves this server's Bedrock port to a free one: when it shares its port with another server, only one of the two can be reached and the other looks broken for no visible reason.
public Task MoveBedrockPortAsync(string? playitKey)
Parameters
playitKeystringA key that may create tunnels, or null when the only one available can only read. Then the console says exactly which tunnel to make on playit.gg instead of failing quietly.
Returns
Remarks
The port picked avoids the other servers' Bedrock ports, the UDP tunnels already on the account and whatever is bound right now — the same search a new crossplay server gets — so moving cannot land on another collision.
RefreshConsoleColours()
Repaints the console and its switches after the colours change in the settings.
public void RefreshConsoleColours()
RefreshFromDisk()
Re-reads data from disk (after editing server.properties).
public void RefreshFromDisk()
RefreshTunnelInfoAsync()
Re-reads this server's tunnel addresses now, instead of waiting for the 30 s timer.
public Task RefreshTunnelInfoAsync()
Returns
SetUpBedrockModContentAsync()
Installs Hydraulic and Fabric API, so Bedrock players see what the mods add.
public Task SetUpBedrockModContentAsync()
Returns
Remarks
The flag is only set once the install has actually succeeded. A server remembering it has this when the download failed would leave Bedrock players staring at invisible blocks with the app insisting it was handled.
SetUpCrossplayAsync(string?)
Installs Geyser and Floodgate, creates the Bedrock tunnel and points Geyser at it.
public Task SetUpCrossplayAsync(string? playitKey)
Parameters
playitKeystring
Returns
Remarks
The order matters. The tunnel has to exist before the config is written, because the one value Geyser cannot work out for itself is the tunnel's public port — and writing the config first would leave it advertising the wrong one until something happened to rewrite it.
SetUpMultiVersionAsync()
Installs ViaVersion and ViaBackwards, so other Minecraft versions can join.
public Task SetUpMultiVersionAsync()
Returns
Remarks
Nothing to configure and no tunnel involved: both plugins work as installed. The flag is only set once the install has actually succeeded — a server remembering it has them when the download failed would turn players away and show no reason for it.
ShutdownAsync()
The mirror of Activate(): stops watching, and stops the server if it is running. Does NOT touch the Playit service, which keeps running in the background.
public Task ShutdownAsync()
Returns
Remarks
Safe on a view model that was never activated, which is the normal case in a test. Only the two unsubscriptions would be wrong to run unpaired, and they are guarded; stopping an inert timer and closing a listener that was never opened cost nothing.
Events
ConfigChanged
Raised when something persistable about the server changes (to save).
public event Action? ConfigChanged