Table of Contents

Namespace McServerLauncher.Services

Classes

AppSettingsService

Loads and saves the global settings in %APPDATA%\McServerLauncher\settings.json.

AtomicDownload

Downloads a file so that a failure can never damage what was already on disk.

Writing straight to the destination truncates it the moment the stream opens, long before the download is known to be good. A connection dropped halfway then leaves a valid path holding a half a file — and for the Fabric server jar it was worse than that: the structural check that runs afterwards deletes what it rejects, so an interrupted loader change turned a working server into one with no jar to start at all.

So the bytes go to "<dest>.part", verification runs against that, and only a file that arrived complete and passed its check replaces the real one — in a single filesystem move. This is the same guarantee AtomicJsonFile gives the app's JSON, applied to binaries.

AtomicJsonFile

Atomic load/save for the app's JSON files (servers.json, settings.json), so a crash or power loss mid-write can never truncate them. Writes go to a ".tmp" file that atomically replaces the target, keeping the previous version as ".bak". A corrupt file is quarantined as ".bad" and the ".bak" copy is restored when possible, instead of silently starting from scratch.

AtomicTextFile

Writing a config file the app owns: only when it actually changed, and never half-written.

BrowserLauncher

Opens a link in the user's browser.

Everywhere else in the app the URL is a constant we wrote. In the store it comes from Modrinth — an author fills in "source", "wiki" or "discord", and the long description is full of links — so it is untrusted input. UseShellExecute hands whatever it is given to the shell, which would happily run a local executable or a registered custom scheme, so only absolute http/https URLs are ever passed through.

BukkitPathRule

The two characters a Paper or Purpur server refuses to run from.

Changelog

Per-version "what's new" notes. When the user updates across several versions at once, the notes for every version they hadn't seen yet are shown together (newest first), so nothing is missed. Add a new entry (newest first) plus its Whatsnew_x_y_z resx keys on each release.

ConsoleColors

The colour each kind of console line is drawn in, as data.

ConsoleLineClassifier

What a console line is about: its severity, and whether it is chat, a player event, or the app.

ConsoleLogService

Persists every server's console output to a shared, dated log file (%APPDATA%/McServerLauncher/logs/launcher-yyyy-MM-dd.log) so the history survives the app being closed or crashing, not just the in-memory console shown in the UI. A new file starts each day; files older than McServerLauncher.Services.ConsoleLogService.RetentionDays are pruned so the folder doesn't grow forever.

ConsolePreferences

The two console colours the user can change, app-wide.

ContentDependencyCheck

Whether every installed mod and plugin has what it needs, answered from the jars alone.

ContentDependencyCheck.Missing

Something needed that nothing installed provides.

ContentManifest

What a mod or plugin jar says about itself: the names it provides, and the ones it needs.

ContentManifest.Manifest

One jar: what it offers under what names, and what it needs to be there.

ContentMigrationService

What happens to the mods or plugins already installed when a server changes family.

CrashReportService

Looks for a Minecraft server crash report relevant to the server's most recent run, to show a short human-readable reason alongside "the server exited unexpectedly" instead of just an exit code. Vanilla/Fabric/Forge/Paper all write crash reports to "<folder>/crash-reports/" with a stable, decade-old format: a "---- Minecraft Crash Report ----" header followed by a "Description: ..." line summarizing what went wrong.

CreateAgentResult

Result of /v1/partner/create_agent: the per-user self-managed agent minted for the user from their setup code. AgentSecretKey is what the app stores (encrypted) and uses as the agent-key for all subsequent tunnel management.

CrossplayService

Setting a server up so people can join from Bedrock as well as Java.

DeathMessageDetector

Detects Minecraft death/kill messages in a server console line, for the "deaths" notification. Best-effort and English/vanilla-oriented: the server's locale or plugins may word some deaths differently. Pairing a death-phrase match with a "subject is a valid player name" check keeps it from firing on chat lines (which start with a "<name>" tag) or other log output.

DesktopShortcutService

Puts a shortcut to the app on the user's desktop.

Each desktop means something different by "shortcut": Windows wants a .lnk, Linux a .desktop entry that has to be executable and, on GNOME, explicitly trusted, and macOS a symlink to the bundle. What they share is that the target must be whatever this copy is really running from — an AppImage the user dropped in Downloads, a bundle in /Applications — and not a guessed install path.

DownloadVerifier

Verifies a downloaded file's hash against the checksum an official API (Mojang, Adoptium, Paper) already returns alongside the download URL. On mismatch the file is deleted and an exception is thrown, so a corrupted or tampered download is never silently trusted.

FileHashCache

The SHA-1 of a file, remembered for as long as the file has not changed.

GeyserConfigService

Geyser's config.yml: where it lives, and the two ports the launcher has to set in it.

GeyserDownloadsApi

GeyserMC's downloads API, which the app reads for Floodgate and for Hydraulic.

GeyserDownloadsApi.Artifact

One published artifact: which build it came from, and how to verify it.

HydraulicService

Hydraulic: what lets Bedrock players actually see the blocks and items that mods add.

ImageCache

Remote images (project icons and gallery screenshots) with a memory + disk cache in front.

The URLs come from Modrinth's API, but they are still remote input, so every download is guarded: the response has to declare an image content-type and stay under a byte cap that is enforced while streaming, so a missing or lying Content-Length can't balloon memory either. Anything odd yields null and the caller keeps its placeholder.

Decoding happens on the calling (background) thread, never on the UI thread. Bitmaps are cached and handed to several views at once, so they are deliberately never disposed here — a disposed bitmap still bound to an Image would throw when it is drawn.

JavaService

Detects the machine's Java installations and, if needed, downloads the right version (Adoptium Temurin) for a specific Minecraft version. Works on Windows and Linux.

JavaService.JavaInstall
LoaderPaths

Where each mod loader leaves the files the launcher has to find again afterwards.

MarkdownBlock

One block of the description: a paragraph, a heading, a list item…

MarkdownParser

A small Markdown reader for the long descriptions Modrinth returns.

It is deliberately partial. Those descriptions are Markdown with raw HTML mixed in, written to be rendered by a web page, and pulling in a full Markdown engine (and an HTML sanitiser behind it) to show them in a desktop panel would be a large dependency for a read-only view. What is supported is what those pages actually use: headings, lists, quotes, code, emphasis, links and rules. Everything else — HTML tags, images, scripts — is dropped rather than shown raw, so a project page can never inject markup into the app.

Images are dropped on purpose: the gallery already shows them, and a description with thirty remote images would mean thirty downloads for a page the user may only skim.

MarkdownSpan

A run of text inside a block, with the formatting that applies to it.

MinecraftRange

Does this Minecraft version satisfy the range a mod declares?

MinecraftVersionService

Gets the list of Minecraft versions from Mojang's official manifest and downloads the server.jar of the chosen version.

MinecraftVersionService.VersionDetails

server.jar URL, required Java version and its official SHA-1 (for integrity checking).

ModDependencyService

The library mods almost every mod needs, and that nobody remembers to install by hand.

ModDependencyService.Needed

One dependency that has to be downloaded, already resolved to a concrete file.

ModDependencyService.Plan

What is missing, and what could not be worked out.

ModLoaderService
ModLoaderService.ForgeInstallResult

Outcome of a Forge server install: a runnable jar (old Forge) or an args id (modern Forge).

ModrinthService
MultiVersionService

Letting people join from a Minecraft version other than the one the server runs.

NameIssue

The problem found, and whatever detail the message needs.

NeoForgeVersions

Turning "which Minecraft version?" into "which NeoForge build?".

NeoForgeVersions.Choice

A chosen build, and whether the user is about to install a pre-release.

NotificationCatalog

What each kind of notification looks like: its level, and the emoji that identifies it.

NotificationCatalog.Entry

One kind of notification: how serious it is, and how it is marked.

NotificationPalette

The default colour of each notification level, and what counts as a valid one.

NotificationPreferences

Decides whether a given notification should be shown for a given server, combining the global settings with an optional per-server override. Global is app-wide state (like ToastService.Shared): set once at startup from AppSettings and updated when the user edits the global notification settings.

PaperService

Downloads the Paper server jar from PaperMC's "fill" API (v3). Paper is a runnable jar, so it launches like vanilla (-jar). Plugins go in the server's plugins/ folder.

PaperService.PaperBuild
PlayersService

Reads the player lists the server stores in JSON files: ops.json (operators), banned-players.json (banned) and usercache.json (known).

PlayitAgentRunner

Runs Playit's official agent (playitd) as a child process so the user's tunnels actually forward traffic — without them installing anything. The binary is downloaded once (pinned to the same version the app registers, McServerLauncher.Services.PlayitAgentRunner.AgentVersion) and launched with --secret-path (see McServerLauncher.Services.PlayitAgentRunner.SecretPath) pointing at the per-user agent key from the setup-code flow. One agent serves all of the user's tunnels; it runs while the app is open and connected, and is stopped on shutdown. App-wide singleton.

PlayitApiException

Error returned by the Playit API (status != success).

PlayitApiService

Playit.gg API client.

  • Preferred: a per-user self-managed agent secret key from the partner setup-code flow (PlayitPartnerService), set app-wide via SetAgentKey(string?) and used as agent-key for both reads AND writes.
  • Fallback (legacy): the agent's secret_key from playit.toml (reads) and a user-pasted write key (writes, sent as Api-Key/Agent-Key). Auth is resolved by trying the schemes in order, so a caller doesn't need to know which kind of key it holds.
PlayitApiService.PlayitTunnel

One tunnel as the agent reports it.

PlayitManager

Playit.gg integration that relies on the background Playit agent already keeping the tunnels up 24/7. The app does not launch its own playit binary: it only queries and, when possible, starts/stops the agent.

  • On Windows the agent is a Windows service ("playitd"), managed via ServiceController.
  • On Linux it is typically a systemd unit, managed via systemctl (start/stop may need privileges).
PlayitPartnerService

Third-party "Integration"-tier onboarding: exchanges a Playit account_setup_code (that the user obtained from playit.gg) for a per-user self-managed agent secret key. The request goes through a small proxy (a Cloudflare Worker, see playit-proxy/) that injects the partner Api-Key server-side — so NO secret ships inside this (public, open-source) app. Everything after uses the returned per-user secret via PlayitApiService.

PortService

Checks which ports are in use on the machine (by any application) and helps find the next free one. TCP and UDP are asked separately because they are separate namespaces.

ProcessStatsService

Computes CPU usage (%) and RAM (MB) of a process, sampling the CPU time between consecutive calls.

ProcessStatsService.Stats
PurpurService

Downloads the Purpur server jar. Purpur is a Paper fork: a runnable jar, plugins in plugins/, and every Bukkit plugin works on it unchanged.

PurpurService.PurpurBuild

A build of Purpur, and the hash their API publishes for it.

SecretProtector

Protects small secrets (like the Playit API key) at rest, on every platform:

  • Windows: DPAPI scoped to the current user, stored as "dpapi:" + base64. Only the same Windows user on the same machine can decrypt it.
  • Linux/macOS: AES-256-GCM with a random per-user key kept in a file only the user can read (0600), stored as "aes:" + base64(nonce | tag | ciphertext). Both protect the settings file against other users and accidental leaks (backups, sharing the file); neither protects against malware already running as the same user — DPAPI can't either.
SelfUpdater

Replaces the running application with an already downloaded and verified package, then restarts it — the same "press Update and it just happens" behaviour on every platform.

Each platform ships differently, so each is applied differently: Windows runs the silent installer, Linux swaps the AppImage file the app is running from, and macOS mounts the .dmg and replaces the .app bundle. What they share is the shape: nothing is touched until a complete, checksum-verified package exists on disk, and any failure leaves the current install working.

ServerCreationService

Generates the initial files of a new server: eula.txt, run.bat and a minimal server.properties with the chosen port (Minecraft fills in the rest on the first launch).

ServerDetectionService

Fills in Type/GameVersion/loader info for servers saved before those fields existed (i.e. GameVersion empty), by inspecting the server folder. Best-effort, runs on load so the mods browser works for older Fabric/Forge servers.

ServerIconService

Generates a server's server-icon.png (64x64 PNG) from any image, cropping it to a centered square and scaling. This is the icon players see in the server list. Uses SkiaSharp so it works on Windows and Linux.

ServerJarInstaller

Puts the right server files on disk for a server type, whichever way that type is obtained.

ServerJarInstaller.InstallResult

What the install left behind, in the shape the caller needs for its config.

ServerNameRule

Checking a server's folder name before it becomes a server that will not start.

ServerProcessManager

Manages the lifecycle of a Minecraft server's java process: startup, clean shutdown (sending "stop" over stdin), command sending and re-emitting the console output in real time.

ServerPropertiesService

Reads and writes the server.properties file (key=value format, one per line). Used to read values (e.g. the port) and to update them from the visual configuration screen.

ServerStorageService

Loads and saves the list of registered servers in %APPDATA%\McServerLauncher\servers.json.

ServerTypeCatalog

One row per server type: what it is called, what it takes, and whether Bedrock can reach it.

ServerTypeCatalog.Entry

Everything the app knows about one server type.

SingleInstance

Keeps the app to one running copy per user, and lets a second launch bring the first one back to the front instead of opening a window of its own.

StoreCache

Two-level cache (memory, then disk, then the network) for the store's API responses. It exists so that opening a mod — and going back and opening it again — costs one request, not one per visit, and so that a project already seen still opens with no connection.

Concurrent callers asking for the same key share a single fetch: opening a details page kicks off the project, its versions and its dependencies at once, and the related-mods strip may ask for a project that is already in flight.

Entries are kept on disk beyond their freshness window on purpose: when the network fails, a stale entry is a much better answer than an empty page. Anything older than McServerLauncher.Services.StoreCache.MaxDiskAge is pruned once per run.

StoreSummaryService

Answers "what does this actually do to my server?" in the user's own language.

Modrinth's own one-line description is written by the author, in English, and often for other modders ("An intermediary api aimed to ease developing multiplatform mods"). So the store ships a hand-written catalogue for the projects a server owner is most likely to meet, in the five languages the app speaks. It is looked up locally: no request, no key, no cost, and it works offline.

Anything outside the catalogue falls back to a sentence built from what Modrinth does tell us — the kind of project, its main tag and, above all, whether players have to install it too, which is the one thing that decides whether a mod is usable on a public server.

The catalogue can be replaced without rebuilding by dropping a store-summaries.json in the user's data folder.

StoreTagService

Turns a store item into the app's own tags.

Modrinth's categories are a good start but they are coarse — nearly half of the top server mods are filed under "utility" — and they don't say the one thing a server owner cares most about: whether players have to install the mod as well. So the categories are combined with keyword rules and with the client/server side, all of which live in Resources/store-tags.json.

The catalogue is read from the embedded copy, unless %APPDATA%\McServerLauncher\store-tags.json exists, in which case that file is used instead. Tags can therefore be added, translated, recoloured or re-mapped without a new build.

UpdateService

Checks the GitHub Releases for a version newer than the installed one.

UpdateService.UpdateInfo

Update data. PackageUrl/PackageName are the package for the platform this app is running on — the Windows installer, the Linux AppImage or the macOS .dmg for this architecture — and are null when the release ships nothing usable here. ChecksumUrl is the asset that carries its SHA-256, used to verify the download before it is installed; null on releases published before that existed.

VerifiedJarDownload

Fetching a server jar: announce the size, download atomically, verify, say it is done.

WakeOnDemandListener

Answers Minecraft clients on the server's port while the real server is stopped, so a stopped server can say "I'm asleep, come in and I'll wake up" instead of just refusing the connection.

It speaks the small, stable part of the protocol needed for that: the handshake, the server-list status, and the login disconnect. Pressing Join is what wakes the server — the client re-pings every few seconds while the multiplayer screen is open, so waking on a status request would start the server over and over for people who are not even playing.

With a Playit tunnel this socket is reachable from the internet, so everything it reads is treated as hostile: lengths are bounded before anything is allocated, every connection has a deadline, and the number of them at once is capped.

WakeStatus

What a client is told about a server that is currently asleep.

WhitelistService

Manages a server's whitelist.json file (list of allowed players). Each entry has uuid + name. It resolves the UUID from Mojang (online mode) or computes it offline when the server does not use official accounts.

WindowBehavior

What the window's minimize and close buttons do, as chosen in the settings. App-wide state (like Global): set at startup from the saved settings and updated when the user edits them, so the change takes effect without restarting the app.

WorldBackupService

Creates and restores zip backups of a server's world folder — the "level-name" directory from server.properties ("world" by default). That single folder holds every dimension, including the modern layout that nests them under "dimensions/", so zipping it alone is a complete backup. Backups live in "<server folder>/backups/"; old ones beyond the configured retention are pruned after each new one.

WorldBackupService.BackupInfo

Structs

Changelog.Section

A version and its localized notes.

Enums

AgentRunState

State of the embedded Playit agent process.

AtomicJsonFile.LoadOutcome

What happened when loading a JSON file.

CrossplayLevel

How well Bedrock players can actually play on a server type.

MarkdownBlockKind

What a parsed block of a project's long description is.

NameIssueKind

What is wrong with a server's folder name, when something is.

PlayitApiService.TunnelEdition

Which edition a tunnel carries: they are different protocols, not a preference.

ServerFamily

What a server type accepts: plugins, mods, or neither.