Brazil's biggest MU Online portal — since 2003
Tutorial Advanced Client

How to set up automatic patching via launcher in MU Online

Build an end-to-end automatic patching flow for the MU Online client — from packaging the changed files to publishing the manifest, with versioning, rollback, and distribution through the launcher.

GA Gabriel · Updated on Aug 20, 2024 · ⏱ 23 min read
Quick answer

Distributing an MU Online patch seems trivial until the day you change three files and need to guarantee that hundreds of players, on different machines and connections, have exactly the same bytes you do. That is when an automatic patching flow stops being a luxury and becomes infrastructure. In th

Distributing an MU Online patch seems trivial until the day you change three files and need to guarantee that hundreds of players, on different machines and connections, have exactly the same bytes you do. That is when an automatic patching flow stops being a luxury and becomes infrastructure. In this guide you will build the complete server-side process — change detection, packaging, versioning, publishing, and rollback — that feeds the launcher running on the player's machine.

This is a client tutorial: it covers the files the player downloads and runs, and how the launcher keeps them in sync. We do not cover the game server configuration itself — if you are still setting up the server, start with the guide on how to create an MU Online server. The focus here is the patch pipeline.

What "automatic patching" really means

Many people call it automatic patching just because the launcher downloads files. That is only half of it. Complete automatic patching has two sides:

  • Server side (build/deploy): a process that compares the current client folder against the last published version, identifies what changed, generates a manifest with hashes, and uploads the changed files to the distribution server.
  • Client side (launcher): the tool that reads the manifest, compares it with what is installed, and downloads only the differences.

The link between the two is the manifest, a JSON file with the list of files and their hashes. When you automate the generation of that manifest, you eliminate the biggest source of bugs on MU servers: the "half-published" patch, with the manifest pointing to a file that was never uploaded or with an outdated hash.

YOU CHANGE FILES
      │
      ▼
[ PATCH SCRIPT ]  ── scans, computes hashes, compares with the previous version
      │              generates the manifest + isolates the changed files
      ▼
[ DISTRIBUTION SERVER ]  (Apache/Nginx with HTTPS)
      │  version.json + /patches/<version>/...
      ▼
[ PLAYER'S LAUNCHER ]  ── downloads only the diff, validates the hash, applies it
      │
      ▼
Updated Main.exe

Prerequisites

ItemRecommendationRole in the flow
Intact base clientComplete MU folder for your seasonSource of truth for the files
Working launcher.NET launcher that consumes the manifestApplies the patch on the player
Distribution serverApache/Nginx + HTTPS, dedicated subdomainHosts the manifest and patches
PowerShell 5+Ships with WindowsRuns the patch script
Compression tool7-Zip (optional)Package large patches
Version controlGit or a versioned folderHistory for rollback

One conceptual prerequisite: define what is distributable. Not everything in the client folder should go into the patch. Logs, the player's local settings (chosen resolution, volume), and temporary files should stay out. Create an exclusion list from the start.

Step 1 — Structure versioning on the server

Before automating, define the folder structure on the distribution server. I recommend versioning by folder, keeping the history:

/var/www/cdn/
├── version.json                 ← CURRENT manifest (what the launcher reads)
├── manifests/
│   ├── 3.1.0.json               ← manifest history (for rollback)
│   ├── 3.2.0.json
│   └── 3.3.0.json
└── patches/
    ├── Data/
    │   ├── Item.bmd
    │   └── Local/Text.bmd
    ├── Interface/
    │   └── loading01.jpg
    └── Main.exe

Here we use a scheme where patches/ always holds the latest version of each file (a mirror of the distributable client), and manifests/ keeps the history. The version.json at the root is simply a copy of the current version's manifest. This simplifies the launcher: it downloads any file at patch_base_url + path and always gets the current version.

> The Data/, Interface/, Main.exe structure is a typical Season 6 example. The actual folder layout and which BMD files exist vary by season/client. Adjust the exclusion list and the paths to your distribution.

Step 2 — The automatic patch script

The heart of the system is a script that does it all: scans, computes hashes, compares with the previous version, assembles the manifest, and reports what changed. This PowerShell script is the template:

# publicar-patch.ps1
param(
    [Parameter(Mandatory)] [string] $NovaVersao   # e.g.: "3.3.0"
)

$clienteDir  = "C:\Build\ClienteMU"      # distributable client (source)
$saidaDir    = "C:\Build\Publicar"       # what will be uploaded to the server
$baseUrl     = "https://cdn.meuservidor.com/patches/"
$exclusoes   = @("*.log", "config.local.ini", "Screenshots\*", "*.tmp")

# --- 1. Scan files, applying exclusions ---
$arquivos = Get-ChildItem $clienteDir -Recurse -File | Where-Object {
    $rel = $_.FullName.Substring($clienteDir.Length + 1)
    -not ($exclusoes | Where-Object { $rel -like $_ })
}

# --- 2. Compute the hash of each file ---
$lista = foreach ($f in $arquivos) {
    $rel  = $f.FullName.Substring($clienteDir.Length + 1).Replace('\','/')
    $hash = (Get-FileHash $f.FullName -Algorithm SHA256).Hash.ToLower()
    [ordered]@{ path = $rel; hash = $hash; size = $f.Length }
}

# --- 3. Compare with the previous manifest (diff) ---
$anterior = @{}
if (Test-Path ".\version.json") {
    (Get-Content ".\version.json" -Raw | ConvertFrom-Json).files |
        ForEach-Object { $anterior[$_.path] = $_.hash }
}

$alterados = $lista | Where-Object { $anterior[$_.path] -ne $_.hash }
Write-Host "Changed/new files: $($alterados.Count)"

# --- 4. Copy only the changed files to the publish folder ---
if (Test-Path $saidaDir) { Remove-Item $saidaDir -Recurse -Force }
foreach ($a in $alterados) {
    $origem  = Join-Path $clienteDir ($a.path -replace '/','\')
    $destino = Join-Path $saidaDir  ($a.path -replace '/','\')
    New-Item -ItemType Directory -Force -Path (Split-Path $destino) | Out-Null
    Copy-Item $origem $destino
}

# --- 5. Generate the new manifest ---
$manifesto = [ordered]@{
    version        = $NovaVersao
    patch_base_url = $baseUrl
    main_exe       = "Main.exe"
    files          = $lista
}
$json = $manifesto | ConvertTo-Json -Depth 4
$json | Out-File ".\version.json"           -Encoding utf8
$json | Out-File ".\manifests\$NovaVersao.json" -Encoding utf8

Write-Host "Patch $NovaVersao ready. Upload:"
Write-Host " - contents of $saidaDir  ->  /var/www/cdn/patches/"
Write-Host " - version.json           ->  /var/www/cdn/version.json"

What this script guarantees:

  1. Automatic detection of what changed, by comparing hashes with the previous manifest — no more manually remembering which files you edited.
  2. Incremental packaging: only the changed files go into the publish folder, reducing the upload.
  3. Always-consistent manifest: the hashes come from the same scan that generated the list, so they are never out of sync.
  4. Versioned history in manifests/, a prerequisite for rollback.

Step 3 — Publishing order (the golden rule)

The number one cause of a broken client during a patch is the wrong upload order. The launcher may fetch the version.json at the exact moment you are uploading the files. If it grabs the new manifest before the files exist, it gets a 404 and fails.

The correct order is always:

  1. Upload all the patch files to /patches/ first.
  2. Confirm they are accessible (curl -I on a few of them).
  3. Only then upload the updated version.json.
# in your terminal, after running the script:
# 1) files first
rsync -avz C:/Build/Publicar/ usuario@servidor:/var/www/cdn/patches/

# 2) verify
curl -I https://cdn.meuservidor.com/patches/Main.exe   # expect 200

# 3) manifest last
scp version.json usuario@servidor:/var/www/cdn/version.json

That way, throughout the whole process players see the old (consistent) manifest until the moment the new one goes live with everything already available.

Step 4 — Configuring the distribution server

The web server needs to serve the files as static content, with two things to watch out for: no cache on the manifest and cache on the patches (which are immutable by hash). Example Nginx configuration:

server {
    listen 443 ssl;
    server_name cdn.meuservidor.com;

    root /var/www/cdn;

    # the manifest must never be cached
    location = /version.json {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";
    }

    # patches can be cached for a long time
    location /patches/ {
        add_header Cache-Control "public, max-age=604800";
    }
}

If you use a CDN in front, respect the same logic: the version.json should have a very low TTL, and the files in patches/ can have a long TTL, since any content change alters the hash and therefore the launcher's behavior.

Step 5 — Rollback when a patch breaks

Sooner or later a patch will go out bad: a corrupted texture, a Main.exe from the wrong season, a Data file with an invalid format. Since the system is hash-based and you kept the history, rollback is quick.

Rollback strategy in three steps:

  1. Identify the last stable version (e.g.: 3.2.0).
  2. Ensure the files from that version still exist in patches/ — if the bad patch overwrote any, restore them from your versioned build.
  3. Republish the stable manifest as version.json:
# revert to 3.2.0
cp /var/www/cdn/manifests/3.2.0.json /var/www/cdn/version.json

On the next launch, the launcher compares the players' current hashes with the 3.2.0 manifest, notices the divergence in the affected files, and downloads the stable versions back. The player never even needs to know there was a problem.

> Always keep at least the last two or three complete versions of the distributable client. Rollback only works if the old bytes still exist somewhere.

Step 6 — Communicating the patch to the player

A good patch flow also informs. Publish a news.json that the launcher displays, with the changelog:

{
  "version": "3.3.0",
  "date": "2026-07-10",
  "highlights": [
    "New seasonal event enabled",
    "Texture fix on the Kanturu map",
    "Drop balance adjustment"
  ]
}

Showing the changelog reduces support tickets ("what changed?") and conveys the sense of an active, well-maintained server — which retains players.

Common errors and fixes

SymptomLikely causeFix
Launcher gets a 404 during the patchManifest uploaded before the filesUpload files first, version.json last
Patch downloads files that did not changeHash comparison fails (case or line endings)Normalize hashes to lowercase; do not accidentally alter binary files
Players stuck on an old versionversion.json cached by CDN/proxyCache-Control: no-cache on the manifest; purge the CDN
Client breaks after a patchCorrupted or wrong-season file publishedRoll back to the previous stable manifest
Slow upload on every patchSending the entire client instead of the diffUse the script that copies only the changed files
Rollback does not restore filesOld bytes were overwritten and lostKeep versioned builds of the latest versions
Manifest and files out of syncManifest generated before finishing the editsAlways generate the manifest last, in the same scan

Patch flow best practices

  • Automate everything into one command: publicar-patch.ps1 3.3.0 should perform the complete build. Manual steps are where errors are born.
  • Test in a mirror folder before publishing to production. Point a test launcher at a staging manifest.
  • HTTPS all the way, from the manifest to the patches. A patch without TLS is a vector for injecting malware into the client.
  • Version the distributable client (Git LFS or a dated folder) to enable reliable rollback.
  • Never include the player's local files in the patch (video config, sound, screenshots). Use the exclusion list.
  • Log every publish: version, date, changed files. That log saves investigations when something breaks.

Release checklist

  • Server folder structure defined (version.json, manifests/, patches/)
  • publicar-patch.ps1 script tested and generating a correct diff
  • Exclusion list covering logs and the player's local configs
  • Upload order respected (files before the manifest)
  • Cache-Control: no-cache on version.json; long cache on patches/
  • HTTPS active on the distribution subdomain
  • Manifest history kept for rollback
  • Versioned build of the latest client versions
  • Rollback procedure tested in a staging environment
  • news.json/changelog published alongside the patch
  • Test launcher validating the patch before releasing it to everyone
  • Publish log updated after each patch

Frequently asked questions

What is the difference between automatic patching and an ordinary launcher?

The launcher is the tool that runs on the player's machine. Automatic patching is the complete server-side process: detecting what changed, packaging, versioning, and publishing the manifest that the launcher consumes. One without the other is incomplete.

Do I need to resend the entire client on every patch?

No. The whole point of automatic patching is to send only the changed files. The manifest lists the hash of each file and the launcher downloads only the differences, saving the player's bandwidth and time.

How do I roll back if a patch breaks the client?

Keep previous versions of the manifest and the files. To revert, republish the manifest pointing to the hashes of the previous stable version. Since the launcher trusts the hash, it restores the old files automatically.

Can I automate everything with a script?

Yes, and it is recommended. A script scans the client folder, computes hashes, compares against the last version, packages the changed ones, and uploads the manifest and files. That turns patching into a single command and eliminates human error.

Does automatic patching depend on the server's season?

The logic is the same for any season. What changes is which files exist in the client folder and the folder structure (Data, BMD textures, Main.exe). Treat these paths as an example, since they vary by season/client.

GA
Guides & builds editor

Gabriel covers gameplay, class builds, PvP and progression. He tests every strategy on a live server before publishing.

Keep reading

Related articles