Skip to content

Conversation

@erikpaasonen
Copy link

@erikpaasonen erikpaasonen commented Dec 27, 2025

Hello! 👋

I recently discovered MSM while looking for tooling to run Minecraft worlds from RAM. The RAM disk feature was exactly what I needed, but I also needed Fabric mod loader support, which MSM doesn't currently have. Rather than extending the bash scripts, I chose to reimplement MSM in Go — partly because that's where I'm more comfortable, and partly because I wanted static typing and easier testing for the features I was adding.

I want to be upfront: I have no expectation that you'll want this merged. Rewriting a project in a different language is a big decision with maintenance implications, not the least of which is the ~6.5K lines of Go (~2.4K tests, ~2.2K docs) vs. the ~4K lines of shell script. I completely understand if bash is the right choice for MSM's future. I'm happy maintaining my own fork for my own personal use if this isn't a direction you want to take.

New features:

  • Fabric mod loader support — Native integration with version compatibility checks; blocks Minecraft upgrades if Fabric doesn't support the new version yet
  • Safer RAM disk — Background sync to disk every 2 minutes to limit data loss on shutdown
  • Shell completion — Auto-generated for Bash, Zsh, and Fish

What's preserved:

  • /etc/msm.conf configuration format (KEY="value" style)
  • Per-server server.conf format
  • Directory structure (/opt/msm/servers, /opt/msm/jars, etc.)

What would change:

  • Build: Requires Go 1.21+ to compile (or use pre-built binaries)
  • Versioning files: Converted from .sh to .yaml format
  • Commands renamed: whitelist/blacklist → allowlist/blocklist
  • Commands restructured: msm op/msm deop → msm op add/remove/list
  • Minimum Minecraft version: 1.7.0+ (dropped support for 1.6.x and earlier to avoid the multi log file/path/format complexity)

If you're interested, I'm happy to discuss or make adjustments. If not, no hard feelings!

  Remaining (Acceptable):

  | Pattern                         | Reason                                                    |
  |---------------------------------|-----------------------------------------------------------|
  | defer file.Close() on reads     | Standard Go pattern - close errors on reads rarely matter |
  | os.RemoveAll in tests           | Test cleanup - acceptable to ignore                       |
  | s.Say/SaveOff/SaveOn in backups | Best-effort server notifications - non-critical           |

  Code Quality Improvements:

  - Config parsing now logs warnings for invalid integer values
  - Server config parsing now logs warnings for invalid values
  - Screen PID parsing now properly handles parse errors
  - All SendCommand calls in allowlist/blocklist now log failures
also dropped pre-1.7 Minecraft support due to a different log file and format, to avoid complexity
  Key Features

  1. Wrapper architecture: Fabric is a per-server flag, not a jar group type
  2. Version detection: Automatically extracts MC version from jar filenames
  3. Upgrade protection: Blocks MC version changes if Fabric doesn't support them (with --force override)
  4. Caching: API responses cached with configurable TTL; JAR files cached indefinitely
  5. Minimal API calls: Only on version changes or explicit commands, never on start/stop
  | Test                                       | Result                                                |
  |--------------------------------------------|-------------------------------------------------------|
  | msm fabric versions - list MC versions     | ✅ Fetches from meta.fabricmc.net                     |
  | msm fabric versions 1.21.11 - list loaders | ✅ Returns loader versions                            |
  | Cache persistence                          | ✅ cache.json created and populated                   |
  | Version detection from jar filename        | ✅ Detected 1.21.11 from minecraft_server.1.21.11.jar |
  | msm fabric status                          | ✅ Shows MC version, loader status, cached JAR        |
  | msm fabric update - JAR download           | ✅ Downloaded 175KB Fabric launcher JAR               |
  | Downloaded JAR validity                    | ✅ Valid ZIP/JAR with Fabric classes                  |
  | Upgrade protection                         | ✅ Blocked upgrade to unsupported version 99.0.0      |
  | --force override                           | ✅ Bypasses protection                                |
  - After=network.target local-fs.target - Proper boot ordering
  - Wants=network.target - Soft dependency (won't fail if network is slow)
  - TimeoutStopSec=120 - Graceful shutdown window for player warnings + world saves

  The Type=oneshot with RemainAfterExit=yes is correct for this use case since the actual Minecraft processes run in screen sessions, not as direct systemd children.
  - Requirements moved to top (before installation)
  - Prerequisites section with OS-specific package install commands
  - System setup section (creating minecraft user and directories)
  - "From Source (Recommended)" with note about automatic setup
  - Pre-built binaries section with trade-off callout
  - Common Commands section (trimmed essentials)
  - Full command reference linked to separate file
  - Upgrading from Bash MSM moved toward bottom
for use with multiple host hardware admin users with access to create/manage their own MC worlds, all started/stopped at boot time by the systemd service.
  | Check                         | Action if needed                |
  |-------------------------------|---------------------------------|
  | Group doesn't exist           | Create it                       |
  | User doesn't exist            | Create it                       |
  | User not in minecraft group   | usermod -aG minecraft minecraft |
  | User's primary group wrong    | usermod -g minecraft minecraft  |
  | User's home dir wrong         | usermod -d /opt/msm minecraft   |
  | Directories missing           | Create them                     |
  | Directory ownership wrong     | Fix only affected directories   |
  | Permissions wrong on servers/ | Fix setgid                      |
  | Command     | Before (ambiguous)                       | After (explicit)                        |
  |-------------|------------------------------------------|-----------------------------------------|
  | RAM status  | msm worlds ram <s> <w> (toggled!)        | msm worlds ram <s> <w> (shows status)   |
  | RAM enable  | (same command toggled)                   | msm worlds ram on <s> <w>               |
  | RAM disable | (same command toggled)                   | msm worlds ram off <s> <w>              |
  | Jar status  | N/A                                      | msm jar <server> (shows current)        |
  | Jar link    | msm jar <s> <group> (looked like GET)    | msm jar link <s> <group>                |
  | Config get  | msm server config <s> [key]              | msm server config <s> [key] (unchanged) |
  | Config set  | msm server config <s> <k> <v> (implicit) | msm server config set <s> <k> <v>       |
Status is almost always '(active)' which might imply running to some people, when in fact it only means that it's the world in 'worldstorage' and not 'worldstorage_inactive', which is a legacy carryover from the bash scripts implementation we might remove. instead, it's easier to think of 1:1 servers to worlds and just create more servers as needed.
@erikpaasonen erikpaasonen changed the title feat: 🎉 re-implement the CLI in Golang, add transparent Fabric support feat: 🎉 Go Rewrite of MSM Dec 27, 2025
@erikpaasonen erikpaasonen marked this pull request as ready for review December 27, 2025 03:59
@erikpaasonen erikpaasonen marked this pull request as draft December 27, 2025 14:10
The game mode setting in level.dat can override the MSM setting when new players join, this enforces that the setting managed by the MSM admin applies to all new players joining the server. also fixes 'msm server init' cmd to inject (merge) this setting into existing already-imported server.properties files.
@erikpaasonen
Copy link
Author

erikpaasonen commented Dec 27, 2025

I'll pull this back into Draft for a while longer so I can flush out e.g. behavioral inconsistencies. I'm happy to squash this branch down to "tell the story" if iterating through commits like this is too noisy, just let me know. 👍

Edit to clarify using the Draft state, i.e. when out of Draft I consider this ready.

@erikpaasonen erikpaasonen marked this pull request as ready for review December 27, 2025 18:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant