Add LAN party modpack, server automation, and client install scripts

Covers the Create + Farmer's Delight modpack list, food roadmap pool,
cross-platform client bootstrap (offline account + modpack + auto-join),
and the venue-box server setup (lazymc idle sleep/wake, Chunky pregen,
file distribution).
This commit is contained in:
Mykhailo Ivanchenko
2026-10-23 13:58:29 +02:00
parent 24e0a07060
commit a243213b47
7 changed files with 659 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
# generated locally by server/setup-server.sh — don't commit the actual server/binaries
server/mcserver/
server/lazymc
server/lazymc.toml
# generated locally when building the client modpack — too large / personal for git
server/dist/*.zip
*.zip
+94
View File
@@ -0,0 +1,94 @@
# How to run this at the LAN party
## 1. Prep the modpack instance once, yourself, beforehand
1. Install [Fjord Launcher Unlocked](https://github.com/hero-persson/FjordLauncherUnlocked) normally on your own machine.
2. Create a new instance: Minecraft **1.20.1**, **Forge**. Name it exactly `LANParty` (or change `INSTANCE_NAME`/`$InstanceName` in both scripts to match).
3. Add the mods from `modpack.md` (use the launcher's built-in Modrinth/CurseForge browser, or drop jars straight into the instance's `mods/` folder).
4. **Set up the server and pregenerate first** — do the "Set up the dedicated venue box" and "Pre-generate chunks before the party starts (Chunky)" steps below *before* continuing here. Distant Horizons can only cache real terrain that already exists; it can't backfill detail after the fact.
5. Launch the client, go to **Multiplayer → Add Server**, enter the venue box's LAN IP, and join for real. Set your preferred video settings (these land in `options.txt`). Then fly/walk around the pregenerated build area with a decent render distance and Distant Horizons enabled — this is what actually populates `Distant_Horizons_server_data/` in the instance with real LOD data for that area. Quit when you've covered the zone you pregenerated.
6. 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`, `Distant_Horizons_server_data/`. Uncheck: `logs/`, `crash-reports/`, `screenshots/`, and any asset/library caches — those regenerate automatically on first launch and just bloat the zip. Name the result `modpack.zip`, with the instance's folder (`LANParty/...`) as the zip's top-level entry.
**Caveats for this to actually pay off for participants:**
- Don't rename the server list entry or change Distant Horizons' "Server Folder Name Mode" setting afterward — both are part of the cache key (`"<server name> <ip>:<port>"` by default). Change either and participants' clients won't find the cache you built.
- Don't regenerate/reset the server world after this point. If the actual party world differs from the one you explored, the cached LOD will show stale terrain that doesn't match reality.
- This cache is for *scenery beyond render distance* only — it doesn't substitute for the server having `online-mode=false` or for Chunky's pregen; all three pieces (pregen, DH cache, offline accounts) are independent and all need to land in the final zip/setup.
## 2. Set up the dedicated venue box (one machine, does both jobs)
Run `server/setup-server.sh` on the Linux box you're bringing to the party. It:
- 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.
- Serves `server/dist/` over HTTP via a systemd unit (survives logout, auto-restarts if it dies).
- Opens the relevant ports in `ufw` if it's active.
```bash
cd server
./setup-server.sh
# then copy your client modpack's mod jars into mcserver/mods/, and:
sudo systemctl restart mc-lazymc
```
Put `install.sh`, `install.ps1`, and `modpack.zip` into `server/dist/` (that's what gets served on port 8000).
Find the box's LAN IP (`ip a`) and edit the top of **both** `install.sh` and `install.ps1`:
- `HOST_URL` / `$HostUrl` → `http://<venue-box-lan-ip>:8000`
- `SERVER_ADDR` / `$ServerAddr` → `<venue-box-lan-ip>:25565` (lazymc's public port)
Useful commands during the party:
- `sudo systemctl status mc-lazymc` — check if the MC server is asleep/awake
- `sudo journalctl -u mc-lazymc -f` — watch it wake/sleep live
- `sudo 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 + file server) | 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.properties` **`view-distance`** to 6–8 — this is the single biggest CPU/RAM lever on a modded server, and the Distant Horizons client mod (see `modpack.md`) makes a short server view-distance look fine anyway.
- Set `simulation-distance` a notch below `view-distance` if things chug — it controls how far Create's kinetic networks/entities get ticked, independent of render range.
- `MIN_RAM`/`MAX_RAM` in `setup-server.sh` default to `2G`/`4G` — bump `MAX_RAM` to match the table above for your headcount before running the script (or edit `lazymc.toml`'s `command` line afterward and `sudo 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 chunks before the party starts (Chunky)
Chunky (server-side mod, already listed in `modpack.md`) fills in a radius of real terrain ahead of time so nobody hits on-the-fly generation lag while exploring. Do this once, before participants arrive:
1. Make sure `mcserver/mods/` has the Chunky jar, then `sudo systemctl restart mc-lazymc` and let the server boot (connect once from a client, or `sudo journalctl -u mc-lazymc -f` and wait for "Done").
2. Run the commands below either from the server console (attach via `sudo systemctl stop mc-lazymc && cd mcserver && bash run.sh nogui`, temporarily bypassing lazymc so you get an interactive console) or via RCON (`mcrcon -H 127.0.0.1 -P 25575 -p <password from server.properties> "<command>"`):
```
/chunky center 0 0 # or your build area's coordinates
/chunky radius 800 # 800 blocks ≈ 50 chunks (1 chunk = 16 blocks)
/chunky start
/chunky confirm # if it warns about area size
```
3. Watch progress with `/chunky status`. For ~50 chunks radius this finishes in seconds to a couple minutes depending on the box.
4. If you stopped lazymc to get console access in step 2, restart it afterward: `sudo systemctl start mc-lazymc`.
Repeat with `/chunky world the_nether` / `/chunky world the_end` first if you want the Nether/End build areas pregenerated too (useful since the roadmap sends people there for Nether's/End's Delight dishes).
## 3. Give participants the one-liner
- **Windows:** `irm http://<your-lan-ip>:8000/install.ps1 | iex`
- **Linux / macOS:** `curl -fsSL http://<your-lan-ip>:8000/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, and launches straight into the instance already connected to your server.
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/FjordLauncherUnlocked` ever disappears, swap the repo slug at the top of the scripts for a current fork.
- `install.sh` needs `curl`, `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-extract` once and point `LAUNCHER_BIN` at the extracted `AppRun`.
- Re-running either script is safe/idempotent: it skips re-downloading the modpack if the instance folder already exists, and just overwrites `accounts.json` with 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.sh` pins a Forge build (`47.4.28` for `1.20.1`) — check [files.minecraftforge.net](https://files.minecraftforge.net/net/minecraftforge/forge/index_1.20.1.html) 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.
+184
View File
@@ -0,0 +1,184 @@
# Food Pool — pick your IRL roadmap meals from here
Checklist of every food item I could confirm, grouped by mod/station. Tick the ones you want to put on the in-game quest line (and cook IRL). Items marked `⚠` are ones I could not fully verify from public sources — double-check with JEI in a test world before committing to cooking them.
## Farmer's Delight (base)
### Raw ingredients / crops
- [ ] Tomato
- [ ] Onion
- [ ] Cabbage
- [ ] Rice
### Cutting board
- [ ] Cabbage Leaf
- [ ] Pumpkin Slice
- [ ] Melon Slice
- [ ] Minced Beef
- [ ] Raw Bacon
- [ ] Raw Chicken Cuts
- [ ] Raw Mutton Chops
- [ ] Raw Cod Slice
- [ ] Raw Salmon Slice
- [ ] Raw Porkchop (cut)
- [ ] Raw Pasta
- [ ] Straw
### Furnace / Smoker / Campfire
- [ ] Cooked Bacon
- [ ] Cooked Chicken Cuts
- [ ] Cooked Mutton Chops
- [ ] Cooked Cod Slice
- [ ] Cooked Salmon Slice
- [ ] Beef Patty
- [ ] Fried Egg
- [ ] Bread (vanilla, FD-compatible)
- [ ] Smoked Ham
### Cooking Pot — soups & stews
- [ ] Beef Stew
- [ ] Chicken Soup
- [ ] Vegetable Soup
- [ ] Fish Stew
- [ ] Pumpkin Soup
- [ ] Baked Cod Stew
- [ ] Noodle Soup
- [ ] Onion Soup
- [ ] Bone Broth
- [ ] Mushroom Stew (vanilla, buffed)
- [ ] Beetroot Soup (vanilla, buffed)
- [ ] Rabbit Stew (vanilla, buffed)
### Cooking Pot — rice, pasta & mains
- [ ] Cooked Rice
- [ ] Fried Rice
- [ ] Mushroom Rice
- [ ] Pasta with Meatballs
- [ ] Pasta with Mutton Chop
- [ ] Squid Ink Pasta
- [ ] Vegetable Noodles
- [ ] Ratatouille
- [ ] Cabbage Rolls
- [ ] Dumplings
### Cooking Pot — other
- [ ] Tomato Sauce
- [ ] Hot Cocoa
- [ ] Apple Cider
- [ ] Glow Berry Custard
- [ ] Dog Food (wolf feed, not for humans!)
- [ ] Stuffed Pumpkin (raw, placeable)
### Crafting Table — sandwiches & rolls
- [ ] Egg Sandwich
- [ ] Chicken Sandwich
- [ ] Hamburger
- [ ] Bacon Sandwich
- [ ] Mutton Wrap
- [ ] Barbecue on a Stick
- [ ] Kelp Roll
- [ ] Salmon Roll
- [ ] Cod Roll
### Crafting Table — plates & salads
- [ ] Stuffed Potato
- [ ] Steak and Potatoes
- [ ] Roasted Mutton Chops
- [ ] Grilled Salmon
- [ ] Bacon and Eggs
- [ ] Mixed Salad
- [ ] Fruit Salad
- [ ] Nether Salad
### Crafting Table — pies & sweets
- [ ] Pie Crust (component)
- [ ] Apple Pie
- [ ] Chocolate Pie
- [ ] Sweet Berry Cheesecake
- [ ] Pumpkin Pie (FD-enabled slicing)
- [ ] Melon Popsicle
- [ ] Honey Cookie
- [ ] Sweet Berry Cookie
### Crafting Table — drinks & basics
- [ ] Melon Juice
- [ ] Milk Bottle
- [ ] Wheat Dough (component)
### Feasts (placed, multi-serving centerpieces — great "final dish" quest targets)
- [ ] Roast Chicken (Plate of Roast Chicken)
- [ ] Stuffed Pumpkin (Bowl of Stuffed Pumpkin)
- [ ] Honey Glazed Ham (Plate of Honey Glazed Ham)
- [ ] Shepherd's Pie (Plate of Shepherd's Pie)
- [ ] Gleaming Salad (Bowl of Gleaming Salad)
- [ ] Rice Roll Medley
---
## Nether's Delight
- [ ] Hoglin Loin (raw/cooked)
- [ ] Hoglin Ham
- [ ] Hoglin Hide (crafting component)
- [ ] Nether Skewers
- [ ] Nether Salad (Warped Fungus + Crimson Fungus + bowl)
- [ ] Magma Gelatin ⚠
- [ ] Raw Stuffed Hoglin
- [ ] Stuffed Hoglin (feast, placeable)
- [ ] Plate of Roasted Hoglin
- [ ] Hoglin sausages ⚠
- [ ] Hoglin burgers ⚠
- [ ] Hoglin breakfast plate ⚠
## End's Delight ⚠ (item names unconfirmed — pull the full list from JEI in a test world)
- [ ] End-dimension dishes — Shulker/Chorus-themed, exact names not published. Treat this mod as a "bonus dimension" quest stage and name the actual dish after checking in-game.
## Delightful
- [ ] Marshmallow on a Stick (raw + roasted)
- [ ] S'more
- [ ] Cheeseburger
- [ ] Deluxe Cheeseburger
- [ ] Jelly Bottle
- [ ] Glow Jelly Bottle
- [ ] Nut Butter Bottle
- [ ] Nut Butter and Jelly Sandwich
- [ ] Ender Nectar (utility item, single-use Ender Chest access — not really "food" to serve IRL)
- [ ] Cactus Flesh
- [ ] Cactus Steak
- [ ] Field Salad
- [ ] Acorn
- [ ] Raw Goat / Cooked Goat
- [ ] Raw Venison Chops / Cooked Venison Chops
- [ ] Cantaloupe Slice
- [ ] Salmonberries
- [ ] Salmonberry Pie
- [ ] Salmonberry Ice Cream
- [ ] Matcha Latte
- [ ] Berry Matcha Latte
- [ ] Matcha Ice Cream
- [ ] Green Tea Leaves (ingredient)
- [ ] Overhauled Pumpkin Pie (sliceable)
(Delightful also adds cross-mod recipes like Crab Rangoon / Honey Glazed Walnut if Biomes O'Plenty/Ecologics are present — skip those, we're not installing those mods.)
## Create × Farmer's Delight bridge mods (Delightful Creators / FarmersCreate / Create: Food)
These mostly add **automation recipes** for dishes you already picked above (e.g. Create's Mixer can brew the same stews, the Deployer can run the cutting board) rather than brand-new meals. Worth noting for the roadmap's "build the automated kitchen" quest stage, but not a separate food source:
- [ ] Bottled/Barreled Soups & Stews (fluid form, for automation)
- [ ] Cut Pumpkin Pie (Create-automatable)
- [ ] Honey Cookie / Sweet Berry Cookie (Create-automatable)
- [ ] Chocolate Pie (Create-automatable)
---
## Suggested picks for a 3-day / 10–12h roadmap
A good spread that's realistic to actually cook IRL for a group:
1. **Day 1 (Overworld basics):** Vegetable Soup → Bacon and Eggs → Mixed Salad
2. **Day 2 (mid-game / Nether run):** Pasta with Meatballs or Beef Stew → Hoglin Ham / Stuffed Hoglin
3. **Day 3 (feast finale):** Shepherd's Pie or Honey Glazed Ham feast, served as the literal centerpiece dish
Strike anything above that's impractical to cook for 10–12 people (e.g. skip Dog Food, skip Ender Nectar — not food) and finalize your checklist.
+80
View File
@@ -0,0 +1,80 @@
# LAN party bootstrap for Windows: installs Fjord Launcher Unlocked (DRM-free
# Prism fork), seeds a persistent offline account, drops in the pre-built
# modpack instance, and launches straight into it on the LAN server.
#
# Host this file via: python3 -m http.server 8000 (alongside modpack.zip)
# Participants run: irm http://<host-ip>:8000/install.ps1 | iex
# ---- EDIT ME before hosting ----
$HostUrl = "http://192.168.1.50:8000"
$ModpackUrl = "$HostUrl/modpack.zip"
$InstanceName = "LANParty"
$ServerAddr = "192.168.1.50:25565"
# ---------------------------------
$InstallDir = Join-Path $env:USERPROFILE "mc-lan-party"
New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null
Set-Location $InstallDir
Write-Host "==> Setting up in $InstallDir"
$release = Invoke-RestMethod -Uri "https://api.github.com/repos/hero-persson/FjordLauncherUnlocked/releases/latest"
$asset = $release.assets | Where-Object { $_.name -match "Windows-MSVC-Portable-.*\.zip$" } | Select-Object -First 1
if (-not $asset) {
$asset = $release.assets | Where-Object { $_.name -match "Windows-MinGW-w64-Portable-.*\.zip$" } | Select-Object -First 1
}
if (-not $asset) { throw "Could not find a Windows portable build in the latest Fjord Launcher Unlocked release." }
Write-Host "==> Downloading $($asset.name)"
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile "FjordLauncher.zip"
Expand-Archive -Path "FjordLauncher.zip" -DestinationPath "app" -Force
$LauncherBin = Get-ChildItem -Path "app" -Filter "fjordlauncher.exe" -Recurse | Select-Object -First 1 -ExpandProperty FullName
if (-not $LauncherBin) { throw "fjordlauncher.exe not found after extracting the portable build." }
# ---- offline account: compute Mojang-style offline UUID so playerdata persists across launches ----
$Nick = Read-Host "Minecraft nickname [$env:USERNAME]"
if ([string]::IsNullOrWhiteSpace($Nick)) { $Nick = $env:USERNAME }
function Get-OfflineUuid([string]$name) {
$md5 = [System.Security.Cryptography.MD5]::Create()
$bytes = $md5.ComputeHash([System.Text.Encoding]::UTF8.GetBytes("OfflinePlayer:$name"))
$bytes[6] = ($bytes[6] -band 0x0f) -bor 0x30
$bytes[8] = ($bytes[8] -band 0x3f) -bor 0x80
$hex = -join ($bytes | ForEach-Object { $_.ToString("x2") })
return "{0}-{1}-{2}-{3}-{4}" -f $hex.Substring(0,8), $hex.Substring(8,4), $hex.Substring(12,4), $hex.Substring(16,4), $hex.Substring(20,12)
}
$Uuid = Get-OfflineUuid $Nick
$md5 = [System.Security.Cryptography.MD5]::Create()
$clientTokenBytes = $md5.ComputeHash([System.Text.Encoding]::UTF8.GetBytes("ClientToken:$Nick"))
$ClientToken = -join ($clientTokenBytes | ForEach-Object { $_.ToString("x2") })
$Iat = [int][double]::Parse((Get-Date -UFormat %s))
$accounts = @{
accounts = @(
@{
active = $true
type = "Offline"
profile = @{ id = $Uuid; name = $Nick; capes = @(); skin = @{ id = ""; url = ""; variant = "" } }
entitlement = @{ canPlayMinecraft = $true; ownsMinecraft = $true }
ygg = @{ token = "0"; extra = @{ clientToken = $ClientToken; userName = $Nick }; iat = $Iat }
}
)
formatVersion = 3
}
$accounts | ConvertTo-Json -Depth 6 | Set-Content -Path "accounts.json" -Encoding UTF8
Write-Host "==> Offline account '$Nick' ready (UUID $Uuid)"
# ---- modpack instance ----
New-Item -ItemType Directory -Force -Path "instances" | Out-Null
if (-not (Test-Path (Join-Path "instances" $InstanceName))) {
Write-Host "==> Downloading modpack from $ModpackUrl"
Invoke-WebRequest -Uri $ModpackUrl -OutFile "modpack.zip"
Expand-Archive -Path "modpack.zip" -DestinationPath "instances" -Force
Write-Host "==> Modpack installed to instances\$InstanceName"
} else {
Write-Host "==> Instance already present, skipping download"
}
Write-Host "==> Launching into $InstanceName as $Nick, joining $ServerAddr"
& $LauncherBin --dir $InstallDir --launch $InstanceName --profile $Nick --server $ServerAddr
Executable
+101
View File
@@ -0,0 +1,101 @@
#!/usr/bin/env bash
# LAN party bootstrap: installs Fjord Launcher Unlocked (DRM-free Prism fork),
# seeds a persistent offline account, drops in the pre-built modpack instance,
# and launches straight into it on the LAN server.
#
# Host this file via: python3 -m http.server 8000
# Participants run: curl -fsSL http://<host-ip>:8000/install.sh | bash
set -euo pipefail
# ---- EDIT ME before hosting ----
HOST_URL="http://192.168.1.50:8000" # where THIS script + modpack.zip are served from
MODPACK_URL="$HOST_URL/modpack.zip" # exported Prism instance folder, zipped (see HOSTING.md)
INSTANCE_NAME="LANParty" # must match the top-level folder name inside modpack.zip
SERVER_ADDR="192.168.1.50:25565" # your Minecraft server IP:port
GH_REPO="unmojang/FjordLauncherUnlocked" # fallback if hero-persson's fork disappears; see note below
# ---------------------------------
INSTALL_DIR="$HOME/mc-lan-party"
mkdir -p "$INSTALL_DIR"
cd "$INSTALL_DIR"
echo "==> Setting up in $INSTALL_DIR"
os="$(uname -s)"
arch="$(uname -m)"
api_url="https://api.github.com/repos/hero-persson/FjordLauncherUnlocked/releases/latest"
release_json="$(curl -fsSL "$api_url")"
pick_asset() {
# $1 = grep pattern for the asset name
echo "$release_json" | grep -oE '"browser_download_url": *"[^"]+"' \
| cut -d'"' -f4 | grep -E "$1" | head -n1
}
LAUNCHER_BIN=""
if [[ "$os" == "Linux" ]]; then
if [[ "$arch" == "aarch64" ]]; then
url="$(pick_asset 'Linux-aarch64\.AppImage$')"
else
url="$(pick_asset 'Linux-x86_64\.AppImage$')"
fi
[[ -n "$url" ]] || { echo "Could not find a Linux AppImage in the latest release." >&2; exit 1; }
echo "==> Downloading $url"
curl -fsSL -o FjordLauncher.AppImage "$url"
chmod +x FjordLauncher.AppImage
LAUNCHER_BIN="$INSTALL_DIR/FjordLauncher.AppImage"
elif [[ "$os" == "Darwin" ]]; then
url="$(pick_asset 'macOS-.*\.zip$')"
[[ -n "$url" ]] || { echo "Could not find a macOS build in the latest release." >&2; exit 1; }
echo "==> Downloading $url"
curl -fsSL -o FjordLauncher.zip "$url"
unzip -oq FjordLauncher.zip -d app
app_path="$(find app -maxdepth 2 -name '*.app' | head -n1)"
LAUNCHER_BIN="$app_path/Contents/MacOS/$(ls "$app_path/Contents/MacOS" | head -n1)"
else
echo "Unsupported OS for install.sh: $os. Use install.ps1 on Windows." >&2
exit 1
fi
# ---- offline account: compute Mojang-style offline UUID so playerdata persists across launches ----
read -rp "Minecraft nickname [${USER:-Player}]: " NICK
NICK="${NICK:-${USER:-Player}}"
md5hex="$(printf '%s' "OfflinePlayer:$NICK" | md5sum | cut -d' ' -f1)"
variant_nibble=$(( (16#${md5hex:16:1} & 3) | 8 ))
variant_hex="$(printf '%x' "$variant_nibble")"
patched="${md5hex:0:12}3${md5hex:13:3}${variant_hex}${md5hex:17:15}"
UUID="${patched:0:8}-${patched:8:4}-${patched:12:4}-${patched:16:4}-${patched:20:12}"
CLIENT_TOKEN="$(printf '%s' "ClientToken:$NICK" | md5sum | cut -d' ' -f1)"
cat > accounts.json <<EOF
{
"accounts": [
{
"active": true,
"type": "Offline",
"profile": { "id": "$UUID", "name": "$NICK", "capes": [], "skin": { "id": "", "url": "", "variant": "" } },
"entitlement": { "canPlayMinecraft": true, "ownsMinecraft": true },
"ygg": { "token": "0", "extra": { "clientToken": "$CLIENT_TOKEN", "userName": "$NICK" }, "iat": $(date +%s) }
}
],
"formatVersion": 3
}
EOF
echo "==> Offline account '$NICK' ready (UUID $UUID)"
# ---- modpack instance ----
mkdir -p instances
if [[ ! -d "instances/$INSTANCE_NAME" ]]; then
echo "==> Downloading modpack from $MODPACK_URL"
curl -fsSL -o modpack.zip "$MODPACK_URL"
unzip -oq modpack.zip -d instances
echo "==> Modpack installed to instances/$INSTANCE_NAME"
else
echo "==> Instance already present, skipping download"
fi
echo "==> Launching into $INSTANCE_NAME as $NICK, joining $SERVER_ADDR"
"$LAUNCHER_BIN" --dir "$INSTALL_DIR" --launch "$INSTANCE_NAME" --profile "$NICK" --server "$SERVER_ADDR"
+47
View File
@@ -0,0 +1,47 @@
# LAN Party Modpack — "Create & Delight" (MC 1.20.1, Forge)
Target: **Minecraft 1.20.1, Forge**. This version/loader combo has the widest, most stable support across every mod below — don't mix in NeoForge/Fabric builds.
## Core
| Mod | Why it's in |
|---|---|
| [Create](https://www.curseforge.com/minecraft/mc-mods/create) | The automation/contraptions backbone. Gives the roadmap something to *build* toward (farms, kitchens, conveyor lines) before the food payoff. |
| [Flywheel](https://modrinth.com/mod/flywheel) | Required rendering engine dependency for Create (Forge build). |
| [Just Enough Items (JEI)](https://modrinth.com/mod/jei) | Not food itself, but mandatory — with this many food-adding mods stacked, nobody will find recipes without it. |
## Cooking base
| Mod | Adds |
|---|---|
| [Farmer's Delight](https://modrinth.com/mod/farmers-delight) | The core cooking/farming mod everything else builds on: cutting board, cooking pot, skillet, stews, pies, feasts. |
## Farmer's Delight addons (the "roadmap fuel")
| Mod | Adds |
|---|---|
| [Nether's Delight](https://modrinth.com/mod/nethers-delight) | Nether-themed cooking: Hoglin loin/ham/hide, nether salad, stuffed Hoglin, nether skewers. |
| [End's Delight](https://modrinth.com/mod/ends-delight) | End-dimension culinary content (shulker/chorus-themed dishes). Item list is thin on public docs — verify in-game with JEI before committing recipes to the IRL roadmap. |
| [Delightful](https://modrinth.com/mod/delightful) | Big grab-bag of immersive new foods independent of any dimension: marshmallows/s'mores, cheeseburgers, nut butter & jelly sandwiches, goat/venison, berry pies, matcha. |
| [Delightful Creators](https://modrinth.com/mod/delightful-creators) | Bridges Create ↔ Farmer's Delight: soups/stews as Create fluids, sequenced-assembly "incomplete foods," cookie/pie recipes usable in Create contraptions. |
| [FarmersCreate](https://modrinth.com/mod/farmerscreate) | Lets Create's Mixer/Deployer automate Farmer's Delight cooking-pot and cutting-board recipes (auto-cook the feast, don't stand there clicking). |
## Performance utility
| Mod | Side | Why it's in |
|---|---|---|
| [Distant Horizons](https://modrinth.com/mod/distanthorizons) | Client only | Renders a simplified level-of-detail view of terrain beyond the server's `view-distance`, so players see far-off scenery without the server having to actually load/send those chunks. Does the same job you were picturing from "Bobby" — Bobby itself is **Fabric-only** (no Forge build exists), so Distant Horizons is the Forge equivalent. Caches the LOD data locally per-world, same idea as Bobby's chunk cache. |
| [Chunky](https://www.curseforge.com/minecraft/mc-mods/chunky-pregenerator-forge) | Server only | Pre-generates a fixed radius of chunks around a chosen center (e.g. spawn/build area) **once, before the party**, via console/RCON commands (`/chunky radius`, `/chunky start`) — eliminates on-the-fly world-gen lag while people explore. **Not** a per-player dynamic follower on Forge — that variant ("Chunky Pregenerator") is Fabric-only, same restriction as Bobby. See `HOSTING.md` for the exact commands. |
**Important:** install Distant Horizons in the client instance's `mods/` folder only — do **not** put it in the server's `mods/` folder. It tags itself as client-side in its mod metadata, so Forge won't reject the mismatched mod list between client and server. Keep the server's `server.properties` `view-distance` modest (6–8, see `HOSTING.md` sizing section) and let Distant Horizons fill in the rest visually on each client. Chunky, conversely, only needs to go in the **server's** `mods/` folder — it has no client-side purpose here.
## Optional / stretch (only if the group wants more scope)
| Mod | Adds | Caveat |
|---|---|---|
| [Aether's Delight](https://modrinth.com/mod/the-aethers-delight) | Aether-themed dishes (Holystone/Zanite knives, berry bowls, buffed meals) | Requires installing **The Aether** too — a full extra dimension mod. Only add if someone really wants to make the trip. |
## Why this shape
- Create gives you *progression* (you have to build the farm/kitchen contraption before you "unlock" a dish) — good quest-line material.
- Farmer's Delight + addons give you the actual *food variety* across three dimensions (Overworld/Nether/End) plus a flavor-pack (Delightful) that isn't dimension-gated, so you can pace the 3-day roadmap: early-game Overworld dishes → mid-game Nether run → late-game End dish as the "final boss" meal.
+145
View File
@@ -0,0 +1,145 @@
#!/usr/bin/env bash
# One-time setup for the LAN party's dedicated box: installs a Forge 1.20.1
# Minecraft server, wraps it in lazymc (auto-sleep when empty / auto-wake on
# connect), and serves the client install scripts + modpack.zip over HTTP —
# all as systemd services so the box can run unattended for the 3 days.
#
# Target: a Linux machine (Debian/Ubuntu/etc with systemd) at the venue.
# Run as a normal user with sudo rights: ./setup-server.sh
set -euo pipefail
# ---- EDIT ME ----
FORGE_MC_VERSION="1.20.1"
FORGE_BUILD="47.4.28" # current recommended build for 1.20.1 — check files.minecraftforge.net if this is stale
MC_PUBLIC_PORT=25565 # what players connect to (lazymc listens here)
MC_INTERNAL_PORT=25566 # real server listens here, lazymc proxies to it
FILESERVER_PORT=8000 # install.sh / install.ps1 / modpack.zip served from here
SLEEP_AFTER="5m" # idle time before lazymc stops the real server
MIN_RAM="2G"
MAX_RAM="6G" # 5-8 players; see ../HOSTING.md sizing table — bump to 8G (9-12p) or 10-12G (13-15p)
# ------------------
BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SERVER_DIR="$BASE_DIR/mcserver"
DIST_DIR="$BASE_DIR/dist"
FORGE_VERSION="${FORGE_MC_VERSION}-${FORGE_BUILD}"
command -v java >/dev/null || { echo "Java not found. Install a Java 17+ JDK (e.g. 'sudo apt install -y openjdk-17-jre-headless') then re-run."; exit 1; }
command -v python3 >/dev/null || { echo "python3 not found. Install it (e.g. 'sudo apt install -y python3') then re-run."; exit 1; }
mkdir -p "$SERVER_DIR" "$DIST_DIR"
# ---- 1. Install the Forge server ----
if [[ ! -f "$SERVER_DIR/run.sh" ]]; then
echo "==> Downloading Forge $FORGE_VERSION installer"
installer_url="https://maven.minecraftforge.net/net/minecraftforge/forge/${FORGE_VERSION}/forge-${FORGE_VERSION}-installer.jar"
curl -fsSL -o "$SERVER_DIR/forge-installer.jar" "$installer_url"
read -rp "Do you accept the Minecraft EULA (https://aka.ms/MinecraftEULA)? [y/N] " eula_ans
[[ "$eula_ans" =~ ^[Yy]$ ]] || { echo "EULA not accepted, aborting."; exit 1; }
echo "==> Running Forge server installer"
(cd "$SERVER_DIR" && java -jar forge-installer.jar --installServer)
echo "eula=true" > "$SERVER_DIR/eula.txt"
else
echo "==> Forge server already installed, skipping"
fi
# server.properties: lazymc will rewrite host/port/rcon itself, we just set gameplay-relevant bits
touch "$SERVER_DIR/server.properties"
for kv in "online-mode=false" "motd=LAN Party - Create & Delight" "difficulty=normal" "max-players=20"; do
key="${kv%%=*}"
if grep -q "^${key}=" "$SERVER_DIR/server.properties" 2>/dev/null; then
sed -i "s/^${key}=.*/${kv}/" "$SERVER_DIR/server.properties"
else
echo "$kv" >> "$SERVER_DIR/server.properties"
fi
done
mkdir -p "$SERVER_DIR/mods"
echo "==> NOTE: copy the same mod jars from your client modpack's mods/ folder into $SERVER_DIR/mods/ (Create, Flywheel, Farmer's Delight + addons, JEI, etc.) before first boot."
# ---- 2. Install lazymc ----
if [[ ! -x "$BASE_DIR/lazymc" ]]; then
echo "==> Downloading latest lazymc"
api_url="https://api.github.com/repos/timvisee/lazymc/releases/latest"
release_json="$(curl -fsSL "$api_url")"
asset_url="$(echo "$release_json" | grep -oE '"browser_download_url": *"[^"]+linux-x64"' | cut -d'"' -f4 | head -n1)"
[[ -n "$asset_url" ]] || { echo "Could not find linux-x64 lazymc asset" >&2; exit 1; }
curl -fsSL -o "$BASE_DIR/lazymc" "$asset_url"
chmod +x "$BASE_DIR/lazymc"
else
echo "==> lazymc already present, skipping"
fi
cat > "$BASE_DIR/lazymc.toml" <<EOF
[server]
directory = "$SERVER_DIR"
command = "bash run.sh nogui -Xms${MIN_RAM} -Xmx${MAX_RAM}"
address = "127.0.0.1:${MC_INTERNAL_PORT}"
freeze_process = false
wake_whitelist = false
[public]
address = "0.0.0.0:${MC_PUBLIC_PORT}"
[time]
sleep_after = "${SLEEP_AFTER}"
minimum_online_time = "1m"
[motd]
sleeping = "☀ Server is asleep, join to wake it up"
starting = "⏳ Server is starting, hang tight..."
[rcon]
enabled = true
port = 25575
EOF
echo "==> Wrote lazymc.toml (sleeps after ${SLEEP_AFTER} empty, public port ${MC_PUBLIC_PORT} -> internal ${MC_INTERNAL_PORT})"
# ---- 3. systemd services ----
sudo tee /etc/systemd/system/mc-lazymc.service >/dev/null <<EOF
[Unit]
Description=LAN party Minecraft server (via lazymc)
After=network.target
[Service]
Type=simple
WorkingDirectory=${BASE_DIR}
ExecStart=${BASE_DIR}/lazymc
Restart=on-failure
User=${USER}
[Install]
WantedBy=multi-user.target
EOF
sudo tee /etc/systemd/system/mc-fileserver.service >/dev/null <<EOF
[Unit]
Description=LAN party modpack/install-script file server
After=network.target
[Service]
Type=simple
WorkingDirectory=${DIST_DIR}
ExecStart=/usr/bin/python3 -m http.server ${FILESERVER_PORT}
Restart=on-failure
User=${USER}
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now mc-lazymc.service mc-fileserver.service
echo "==> Done."
echo " - Minecraft server: public port ${MC_PUBLIC_PORT}, sleeps after ${SLEEP_AFTER} idle, wakes on connect."
echo " - File server: http://<this-box-lan-ip>:${FILESERVER_PORT}/ serving ${DIST_DIR}/"
echo " - Put install.sh, install.ps1 and modpack.zip into ${DIST_DIR}/ (see ../HOSTING.md)."
echo " - Copy your client mod jars into ${SERVER_DIR}/mods/ if you haven't yet, then: sudo systemctl restart mc-lazymc"
if command -v ufw >/dev/null && sudo ufw status | grep -q "Status: active"; then
echo "==> ufw is active — opening ${MC_PUBLIC_PORT}/tcp and ${FILESERVER_PORT}/tcp"
sudo ufw allow "${MC_PUBLIC_PORT}/tcp"
sudo ufw allow "${FILESERVER_PORT}/tcp"
fi