Chunky + DH-on-server crashes on first chunk load (confirmed open upstream bug, DH GitLab #1329). DH's own INTERNAL_SERVER generation mode does the same pregen job without the conflict, plus streams accurate LOD to clients automatically (no manual flying pass needed). Corrects Vivecraft/CPM/ViveCraft Compat to both-sides (needed for other players to see VR body movement, not just client-only). Fixes lazymc.toml duration-string bug (sleep_after wants an integer, not "5m"). Replaces the screenshot-derived mod list with ground truth from the actual exported client zip.
12 KiB
How to run this at the LAN party
1. Prep the modpack instance once, yourself, beforehand
- Install Fjord Launcher Unlocked normally on your own machine.
- Create a new instance: Minecraft 1.20.1, Forge. Name it exactly
LANParty(or changeINSTANCE_NAME/$InstanceNamein both scripts to match). - Add the mods from
modpack.md(use the launcher's built-in Modrinth/CurseForge browser, or drop jars straight into the instance'smods/folder). - Set up the server first — do the "The server" and "Pre-generate terrain before the party starts (Distant Horizons Full-mode)" steps below before continuing here.
- Launch the client, go to Multiplayer → Add Server, enter
minecraft.mivanchenko.de:25565, and join for real. Set your preferred video settings (these land inoptions.txt). No manual flying pass needed — Distant Horizons is installed server-side (same build both sides) and streams accurate LOD data directly to any connected client with Distant Generation enabled, so coverage is automatic for every participant, not just something baked in by the host ahead of time. - Quit the launcher, then export the instance via right-click the instance → Export Instance (don't blindly zip the raw folder — the exporter lets you pick what's included). Check:
mods/,config/,options.txt,servers.dat. Uncheck:logs/,crash-reports/,screenshots/,Distant_Horizons_server_data/(not needed — each client builds its own via the server-side streaming), and any asset/library caches — those regenerate automatically on first launch and just bloat the zip. Name the resultmodpack.zip, with the instance's folder (LANParty/...) as the zip's top-level entry.
2. The server: mivanchenko.de (always-on, not a box you carry to the venue)
This setup runs on the existing homelab box at mivanchenko.de (SSH: ssh server, i.e. port 222, same machine already running Gitea/n8n/the SMB stack/etc) rather than a machine physically present at the party. That means no DNS repoint, no file-server migration, no "moving to the venue" step — minecraft.mivanchenko.de already resolves here and stays pointed here.
Tradeoff to be aware of: since the server isn't on the venue's local network, players connect over the internet, not LAN — latency depends on the venue's internet connection, not your switch. For a 3-day modded-Minecraft LAN party this is usually fine (Minecraft is forgiving of 20–60ms), but if the venue's internet is bad, this is the thing that bites. Worth testing from the venue ahead of time if you can.
Server location: ~/lan-minecraft/server/ on the box. Setup:
ssh server
cd ~/lan-minecraft
# copy server/setup-server.sh here (or git clone the mc-lan-party repo), then:
cd server && ./setup-server.sh
# then copy your client modpack's mod jars into mcserver/mods/, and:
sudo systemctl restart mc-lazymc
setup-server.sh installs the Forge 1.20.1 server and wraps it in lazymc, which sleeps the real server after 5 idle minutes and transparently wakes it the instant someone connects — no manual start/stop for the whole 3 days. It does not handle file distribution (see section 3 — that's Gitea now, not this script).
Since port 25565 is reached directly (Minecraft is raw TCP, not HTTP — Caddy on this box only proxies HTTP(S) on 80/443 and has no layer4/TCP module, so it genuinely can't front Minecraft traffic), there's no Caddy entry to add for minecraft.mivanchenko.de. lazymc just binds 0.0.0.0:25565 on the host directly, same way crafty_container already exposes its own Minecraft ports on this box.
Services temporarily stopped on this box to free up resources for the party (docker stop, not removed — bring back with docker start <name> after): smb-crm, smb-db, breathemate-license, crafty_container, n8n-n8n-1, open-webui, mcpo.
Useful commands during the party:
sudo systemctl status mc-lazymc— check if the MC server is asleep/awakesudo journalctl -u mc-lazymc -f— watch it wake/sleep livesudo systemctl restart mc-lazymc— pick up new mods or a config change
Resource sizing for the venue box (5–15 players)
Create is the dominant cost here — it's heavier on the server tick thread (CPU) than on RAM, because kinetic networks and contraptions get simulated every tick regardless of how many players are nearby. Farmer's Delight/its addons are cheap by comparison. Numbers below assume this modpack (~10 mods) and are total host box numbers, not just the JVM heap:
| Concurrent players | JVM heap (-Xmx, set in setup-server.sh) |
Host RAM (total, incl. OS + other running services) | CPU | Free disk | Network |
|---|---|---|---|---|---|
| 5–8 | 6G | 10–12 GB | 4 cores, 3.5 GHz+ single-core perf matters more than core count | ~10 GB (world + mods + Forge libs + logs) | Gigabit LAN recommended, Wi-Fi workable |
| 9–12 | 8G | 12–16 GB | 4–6 cores, 3.5 GHz+ | ~15 GB | Gigabit LAN recommended |
| 13–15 | 10–12G | 16 GB+ | 6+ cores, 3.5 GHz+ | ~20 GB | Gigabit LAN, wire the host box if possible |
Practical knobs to turn if the box is lighter than the table:
- Lower
server.propertiesview-distanceto 6–8 — this is the single biggest CPU/RAM lever on a modded server, and the Distant Horizons client mod (seemodpack.md) makes a short server view-distance look fine anyway. - Set
simulation-distancea notch belowview-distanceif things chug — it controls how far Create's kinetic networks/entities get ticked, independent of render range. MIN_RAM/MAX_RAMinsetup-server.shdefault to2G/4G— bumpMAX_RAMto match the table above for your headcount before running the script (or editlazymc.toml'scommandline afterward andsudo systemctl restart mc-lazymc).- An SSD matters more than raw disk size here — Create's block-entity-heavy contraptions do a lot of small, frequent chunk saves; a spinning HDD will show up as tick-time spikes.
Pre-generate terrain before the party starts (Distant Horizons Full-mode)
Distant Horizons (same build, both client and server mods/) handles pregeneration itself now — Chunky is not used (see modpack.md's Performance section for why). Edit mcserver/config/DistantHorizons.toml, under [common.worldGenerator]:
chunkGeneratorMode = "INTERNAL_SERVER" # default is "FEATURES" (LOD-only, doesn't save real chunks) — change to this
generationCenterChunkX = 0 # or your build area's center, in chunk coords (block coords / 16)
generationCenterChunkZ = 0
generationMaxChunkRadius = 50 # 50 chunks ≈ 800 blocks. 0 (the default) means unbounded/follows render distance instead
INTERNAL_SERVER mode is the one that matters — unlike the other modes, it actually generates and saves real chunks to the region files (not just LOD approximation), which is the same job Chunky would've done.
This only runs while the actual Java process is up, and lazymc will stop it again after the idle timeout if nobody's connected — so for this one-time pregen session, bypass lazymc and run the server directly instead of waiting on the sleep/wake cycle:
sudo systemctl stop mc-lazymc
cd mcserver && bash run.sh nogui
Watch the console for DH-Request Module Ticker / world-gen-queue log lines — generation runs automatically in the background once the world is loaded, no command needed to kick it off (unlike Chunky's /chunky start). Let it run until the console goes quiet / chunks-per-second drops to near zero, then stop from the console (or Ctrl+C) and bring lazymc back: sudo systemctl start mc-lazymc.
Repeat for Nether/End build areas if you want those pregenerated too (useful since the roadmap sends people there for Nether's/End's Delight dishes) — DH generates per-dimension automatically as each is loaded; no separate per-dimension command needed, just make sure you (or a connected client) actually visit those dimensions once during the pregen session to trigger their world-gen queues.
3. File distribution: Gitea, not a local file server
install.sh, install.ps1, and (once built) modpack.zip are committed straight to the mc-lan-party repo on Gitea (git.mivanchenko.de, already proxied by Caddy and public), served via its raw-file URLs. No separate file-server process on the venue/server box — one less moving part, and it's fine for now per your call; worth revisiting (e.g. Gitea releases/LFS, or an object store) if modpack.zip turns out large enough that Gitea's raw-file serving gets awkward.
Give participants:
- Windows:
irm https://git.mivanchenko.de/mivanchenko/mc-lan-party/raw/branch/main/install.ps1 | iex - Linux / macOS:
curl -fsSL https://git.mivanchenko.de/mivanchenko/mc-lan-party/raw/branch/main/install.sh | bash
Each script asks for a nickname, derives a stable offline-mode UUID from it (so the same name = same player data across the whole 3-day party, even if they re-run the script), seeds that into a fresh accounts.json, downloads Fjord Launcher Unlocked + the modpack from Gitea, and launches straight into the instance already connected to minecraft.mivanchenko.de.
Make sure your Minecraft server itself is running with online-mode=false in server.properties — offline accounts only work against offline-mode servers.
Notes / things that can break
- The scripts pull Fjord Launcher Unlocked's latest GitHub release dynamically, so they stay correct even after version bumps — but if
hero-persson/FjordLauncherUnlockedever disappears, swap the repo slug at the top of the scripts for a current fork. install.shneedscurl,unzip/find(standard on any Linux/macOS box) and relies on FUSE for running the AppImage directly; if a participant's distro lacks FUSE, the AppImage will fail to launch — tell them to run./FjordLauncher.AppImage --appimage-extractonce and pointLAUNCHER_BINat the extractedAppRun.- Re-running either script is safe/idempotent: it skips re-downloading the modpack if the instance folder already exists, and just overwrites
accounts.jsonwith the (same, deterministic) account if the nickname is unchanged. - lazymc's first wake after a sleep takes as long as the Forge server normally does to boot (expect 20–60s depending on the box) — the client will show a "starting" MOTD/kick message during that window; just reconnect after a few seconds.
setup-server.shpins a Forge build (47.4.28for1.20.1) — check files.minecraftforge.net if it's gone stale by the time you run this.- Double-check the server's mod list is byte-for-byte the same mods as the client instance (same versions) — Forge is strict about mod-list mismatches between client and server and will refuse to connect otherwise. The cleanest way to guarantee this is pulling server-side jars straight out of the exported client zip (
unzip -j modpack.zip "minecraft/mods/*.jar" -d mcserver/mods), not re-downloading them independently. unzipcan extract files with0000permissions from zips built on Windows (seen when extracting a Prism/Fjord-exported instance zip — the jars came out as----------, unreadable even by their owner). If the server fails to load mods right after extracting a client zip with errors that don't match what actually worked on the client, checkls -la mcserver/mods/first —chmod -R u+rwX mcserver/modsfixes it.- Create bundles Flywheel and Ponder inside its own jar via Forge's JarJar mechanism (visible in the client log as
"Found 11 dependencies adding them to mods collection") — don't go looking for separateflywheel-*.jar/Ponder-*.jarfiles to add tomods/on either side, they don't exist as standalone files in this setup.