My version of remote-media script for Luanti
Backstory
There is a very good doc about setting up a remote media server for Luanti, which is a web server that hosts the hash-named media files that a client will need to download for playing on a server. It reminds me of c_rehash for x.509 certs for openssl. Using a web server for these files lets you take advantage of static file hosting like in nginx or httpd, which is faster than the UDP connections that Luanti uses. Luanti can do it, but nginx will be faster.
There is a simple demo script on that docs page, and it is a great reference point.
My version
files/2025/listings/generate-remote-media.sh (Source)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
#!/bin/sh # File: generate-remote-media.sh # Location: /mnt/public/Support/Games/luanti/scripts/ # Author: sofar, bgstack15 # Startdate: 2025-09-26-6 11:15 # Title: Generate Remote Media # Project: Luanti Remote Media # Purpose: Generate Remote Media Directory for Web Hosting # History: # Usage: # Server setup: minetest.conf `remote_media = https://www.example.com/games/luanti/media/` # RECURRING: # on vm4: time INDIR=/home/luanti/game-avengers1 OUTDIR=/mnt/public/www/games/luanti/media /mnt/public/www/games/luanti/scripts/generate-remote-media.sh # on server3: . /mnt/public/Support/Games/luanti/scripts/generate-remote-media.sh ; time index_dir /mnt/public/www/games/luanti/media # Reference: https://docs.luanti.org/for-server-hosts/remote-media/ # Improve: # Documentation: # Using a remote media server will crash the client if the client is running with --verbose and piping to grep | tee. # The client can remote-fetch .obj, .tr, and .po files, but the reference script does not demonstrate these. This script omits .tr and .po translation files because I do not expect to need them. collect_from() { _indir="${1}" _outdir="${2}" _count="${3}" _start="${4}" x="${_start}" _temp="$( mktemp )" printf '' > "${_temp}" clean_collect_from() { rm -f "${_temp:-NOTHINGTODEL}" trap '' 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 18 19 20 } trap "__ec=$? ; clean_collect_from ; trap '' 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 18 19 20 ; exit ${__ec} ;" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 18 19 20 { echo "Processing media from: ${_indir}" find -L "${_indir}" -type f -name "*.png" -o -name "*.obj" -o -name "*.jpg" -o -name "*.jpeg" -o -name "*.ogg" -o -name "*.x" -o -name "*.b3d" | while read f; do x=$((x+1)) printf "%0${#_count}d/%0${#_count}d " "${x}" "${_count}" ; basename "$f" hash=`openssl dgst -sha1 <"$f" | cut -d " " -f 2` cp "$f" "${_outdir}/$hash" printf '%s' "." >> "${_temp}" done } 1>&2 wc -c "${_temp}" | awk '{print $1}' clean_collect_from } index_dir() { # Usage: index_dir "${OUTDIR}" _outdir="${1}" printf "Creating index.mth... " printf "MTHS\x00\x01" > "${_outdir}/index.mth" find "${_outdir}" -type f -not -name index.mth | while read f; do openssl dgst -binary -sha1 <$f >> "${_outdir}/index.mth" done } main() { INDIR="${INDIR:-/home/luanti/game-avengers1}" OUTDIR="${OUTDIR:-/mnt/public/www/games/luanti/media}" mkdir -p "${OUTDIR}" # Change this 'collect_from' or add more lines of 'collect_from', the script will recursively # search for files with extensions of Luanti media in this folder. # This is an example to be run in a game folder, but you can change this to anything to catch # all textures that a server uses. # My indir will be the /home/luanti/game-avengers1/ style directory, so we only need mods and games. I do not use textures in my worlds. _count="$( find -L "${INDIR}/mods" "${INDIR}/games" -type f -name "*.png" -o -name "*.obj" -o -name "*.jpg" -o -name "*.jpeg" -o -name "*.ogg" -o -name "*.x" -o -name "*.b3d" | wc -l )" _current="$( collect_from "${INDIR}/mods" "${OUTDIR}" "${_count}" 0 )" echo "Got result _current ${_current}" _current="$( collect_from "${INDIR}/games" "${OUTDIR}" "${_count}" "${_current}" )" # Example for MineClone2 (they put textures in textures/ now) #collect_from mods/ #collect_from textures/ # You should run index_dir on the server that owns the hashed filenames. Doing it over nfs is slower. #index_dir "${OUTDIR}" } # BEGIN IS-SOURCED sourced=0 if [ -n "$ZSH_VERSION" ]; then case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac elif [ -n "$KSH_VERSION" ]; then [ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ] && sourced=1 elif [ -n "$BASH_VERSION" ]; then (return 0 2>/dev/null) && sourced=1 else # All other shells: examine $0 for known shell binary filenames. # Detects `sh` and `dash`; add additional shell filenames as needed. case ${0##*/} in sh|-sh|dash|-dash) sourced=1;; esac fi # END BEGIN IS-SOURCED if test "${sourced}" = "0" ; then main fi |
This script is designed to be run on the Luanti server, and on the separate web server. Hashing a few thousand files works way better when doing it against a local filesystem than on nfs!
I do not include the .tr and .po translation files, which are eligible for being pulled from a remote media server, because I do not need them. It triples (as in from 3,000 objects to 9,000 objects) the number of assets I needed to prepare, so it's an optimization I made for myself.
So on the game server, for each instance/world, I run the following.
time INDIR=/home/luanti/game-avengers1 OUTDIR=/mnt/public/www/games/luanti/media /mnt/public/www/games/luanti/scripts/generate-remote-media.sh
And then on the one web server, just once, I run:
. /mnt/public/Support/Games/luanti/scripts/generate-remote-media.sh ; time index_dir /mnt/public/www/games/luanti/media
Using a remote media server
And then of course to use this, you need the clients configured to use remote media server in minetest.conf.
# Default = true enable_remote_media_server = true
And the game server needs to tell the clients where to get such media, also in minetest.conf.
# default is empty. Be sure to end with a slash here. remote_media = http://www.example.com/games/luanti/media/
Comments