#!/bin/bash # Lade globale Konfiguration CONFIG_FILE_PATH="$(dirname "$0")/config.sh" if [ -f "$CONFIG_FILE_PATH" ]; then source "$CONFIG_FILE_PATH" else echo "FEHLER: Globale Konfigurationsdatei config.sh nicht gefunden!" exit 1 fi # === Logging Funktion (früh definieren für Wrapper-Logik) === LOG_FILE_BASE="${LOG_DIR_BASE}/$(basename "$0" .sh)" log_message() { local key="${1:-main}" local msg="$2" local log_target="${LOG_FILE_BASE}.log" [ "$key" != "main" ] && log_target="${LOG_FILE_BASE}_${key}.log" local message_to_log; message_to_log="$(date '+%Y-%m-%d %H:%M:%S') - [${key}] - ${msg}" echo "${message_to_log}" | tee -a "$log_target" } # --- Wrapper-Logik zur Verarbeitung aller Welten, wenn kein Argument übergeben wird --- if [ -z "$1" ]; then log_message "main" "Kein spezifischer Welt-Schlüssel angegeben. Verarbeite alle Welten mit web.conf..." shopt -s nullglob for world_dir in "${MINETESTMAPPER_WORLD_DATA_BASE_PATH}"/*/; do if [ -f "${world_dir}web.conf" ]; then world_key_to_process=$(basename "$world_dir") log_message "main" "--- Starte Durchlauf für '${world_key_to_process}' ---" bash "$0" "$world_key_to_process" fi done shopt -u nullglob log_message "main" "Alle Welten verarbeitet." exit 0 fi # ############################################################################# # Ab hier beginnt die Logik für eine EINZELNE Welt # ############################################################################# /opt/luweb/check_dependencies.sh || exit 1 WORLD_KEY=$1 CURRENT_MINETEST_WORLD_DATA_PATH="${MINETESTMAPPER_WORLD_DATA_BASE_PATH}${WORLD_KEY}/" if [ ! -d "$CURRENT_MINETEST_WORLD_DATA_PATH" ]; then log_message "${WORLD_KEY}" "FEHLER: Das Welt-Datenverzeichnis '${CURRENT_MINETEST_WORLD_DATA_PATH}' wurde nicht gefunden!" exit 1 fi if [ ! -f "${CURRENT_MINETEST_WORLD_DATA_PATH}world.mt" ]; then log_message "${WORLD_KEY}" "FEHLER: Die Datei 'world.mt' wurde im Verzeichnis '${CURRENT_MINETEST_WORLD_DATA_PATH}' nicht gefunden!" exit 1 fi # === Lade Standardwerte und überschreibe mit welt-spezifischer web.conf === MM_OPT_ZOOM_LEVEL="$DEFAULT_MM_OPT_ZOOM_LEVEL"; MM_OPT_MIN_Y="$DEFAULT_MM_OPT_MIN_Y" MM_OPT_ORIGINCOLOR="$DEFAULT_MM_OPT_ORIGINCOLOR"; MM_OPT_PLAYERCOLOR="$DEFAULT_MM_OPT_PLAYERCOLOR" MM_OPT_SCALECOLOR="$DEFAULT_MM_OPT_SCALECOLOR"; MM_OPT_BGCOLOR="$DEFAULT_MM_OPT_BGCOLOR" MM_CFG_DRAWALPHA="$DEFAULT_MM_CFG_DRAWALPHA"; MM_CFG_DRAWORIGIN="false" MM_CFG_DRAWPLAYERS="$DEFAULT_MM_CFG_DRAWPLAYERS"; MM_CFG_DRAWSCALE="false" TILES_SUBDIR_NAME="$DEFAULT_TILES_SUBDIR_NAME"; GDAL2TILES_ZOOM_LEVELS="$DEFAULT_GDAL2TILES_ZOOM_LEVELS" WEB_MAP_PNG_FILENAME="$DEFAULT_WEB_MAP_PNG_FILENAME"; RESIZED_MAX_DIMENSION="$DEFAULT_RESIZED_MAX_DIMENSION" ARCHIVE_SUBDIR_NAME="$DEFAULT_ARCHIVE_SUBDIR_NAME" WORLD_WEB_CONFIG_FILE="${CURRENT_MINETEST_WORLD_DATA_PATH}web.conf" if [ -f "$WORLD_WEB_CONFIG_FILE" ]; then log_message "${WORLD_KEY}" "Lade Web-Konfiguration aus ${WORLD_WEB_CONFIG_FILE}" source "$WORLD_WEB_CONFIG_FILE" fi # === Abgeleitete Variablen === SCRIPT_BASENAME=$(basename "$0" .sh) MINETESTMAPPER_PATH="${BASE_SCRIPT_DIR}/${MINETESTMAPPER_EXEC_NAME}" LOG_FILE="${LOG_DIR_BASE}/${SCRIPT_BASENAME}_${WORLD_KEY}.log" LOCK_FILE="${LOCK_FILE_BASE_DIR}/${SCRIPT_BASENAME}_${WORLD_KEY}.lock" RAW_MAP_OUTPUT_DIR_ABSOLUTE="${BASE_SCRIPT_DIR}/${RAW_MAP_BASE_SUBDIR}/${WORLD_KEY}" RAW_MAP_ABSOLUTE_PATH="${RAW_MAP_OUTPUT_DIR_ABSOLUTE}/${RAW_MAP_FILENAME}" UNKNOWN_NODES_FILE_ABSOLUTE_PATH="${RAW_MAP_OUTPUT_DIR_ABSOLUTE}/unknown_nodes.txt" MAP_INFO_FILE_ABSOLUTE_PATH="${RAW_MAP_OUTPUT_DIR_ABSOLUTE}/map_info.txt" WEB_CURRENT_WORLD_DIR="${WEB_ROOT_PATH}/${WEB_MAPS_BASE_SUBDIR}/${WORLD_KEY}" TILES_FULL_OUTPUT_PATH="${WEB_CURRENT_WORLD_DIR}/${TILES_SUBDIR_NAME}" WEB_MAP_PNG_FULL_PATH="${WEB_CURRENT_WORLD_DIR}/${WEB_MAP_PNG_FILENAME}" ARCHIVE_BASE_WEB_PATH="${WEB_CURRENT_WORLD_DIR}/${ARCHIVE_SUBDIR_NAME}" # === Funktion zur Archivbereinigung === prune_archives() { log_message "${WORLD_KEY}" "Starte Archivbereinigung..." if [ ! -d "$ARCHIVE_BASE_WEB_PATH" ]; then return; fi local cutoff_date_14_days=$(date -d "today - 14 days" +%Y-%m-%d) find "$ARCHIVE_BASE_WEB_PATH" -type f -name "*.png" | while IFS= read -r archive_file_path; do if [[ "$archive_file_path" =~ /([0-9]{4})/([0-9]{2})/([0-9]{2})\.png$ ]]; then local img_date_str="${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-${BASH_REMATCH[3]}" if ! date -d "$img_date_str" "+%s" >/dev/null 2>&1; then continue; fi if [ "$(date -d "$img_date_str" +%s)" -ge "$(date -d "$cutoff_date_14_days" +%s)" ]; then continue; fi if [ "$(date -d "$img_date_str" +%u)" -ne 1 ]; then log_message "${WORLD_KEY}" "LÖSCHE (>14 Tage, kein Montag): ${archive_file_path}" rm -f "$archive_file_path" fi fi done find "$ARCHIVE_BASE_WEB_PATH" -mindepth 1 -type d -empty -delete } # === Hauptlogik für eine einzelne Welt === exec 200>"$LOCK_FILE" flock -n 200 || { log_message "${WORLD_KEY}" "Script bereits aktiv. Beende."; exit 1; } trap 'rm -f "$LOCK_FILE"; log_message "${WORLD_KEY}" "Skript beendet."' EXIT log_message "${WORLD_KEY}" "Skript gestartet." mkdir -p "${RAW_MAP_OUTPUT_DIR_ABSOLUTE}" log_message "${WORLD_KEY}" "Starte minetestmapper..." MM_ALL_OPTIONS_STR="--zoom ${MM_OPT_ZOOM_LEVEL} --min-y ${MM_OPT_MIN_Y}" if [ "${MM_CFG_DRAWALPHA}" = "true" ]; then MM_ALL_OPTIONS_STR="${MM_ALL_OPTIONS_STR} --drawalpha"; fi if [ "${MM_CFG_DRAWPLAYERS}" = "true" ]; then MM_ALL_OPTIONS_STR="${MM_ALL_OPTIONS_STR} --drawplayers"; fi MM_ALL_OPTIONS_STR="${MM_ALL_OPTIONS_STR} --origincolor '${MM_OPT_ORIGINCOLOR}' --playercolor '${MM_OPT_PLAYERCOLOR}'" MM_ALL_OPTIONS_STR="${MM_ALL_OPTIONS_STR} --scalecolor '${MM_OPT_SCALECOLOR}' --bgcolor '${MM_OPT_BGCOLOR}'" MAP_GENERATION_COMMAND="'${MINETESTMAPPER_PATH}' -i '${CURRENT_MINETEST_WORLD_DATA_PATH}' -o '${RAW_MAP_ABSOLUTE_PATH}' ${MM_ALL_OPTIONS_STR}" MAPPER_RUN_OUTPUT_CAPTURE_FILE=$(mktemp) (set -o pipefail; eval "${MAP_GENERATION_COMMAND}" 2>&1 | tee -a "$LOG_FILE" > "$MAPPER_RUN_OUTPUT_CAPTURE_FILE"); MAPPER_EXIT_STATUS=$? if [ ${MAPPER_EXIT_STATUS} -ne 0 ] || [ ! -f "$RAW_MAP_ABSOLUTE_PATH" ]; then log_message "${WORLD_KEY}" "FEHLER: minetestmapper (Status: ${MAPPER_EXIT_STATUS})." rm -f "$MAPPER_RUN_OUTPUT_CAPTURE_FILE"; exit 1; fi log_message "${WORLD_KEY}" "map.png erfolgreich generiert." log_message "${WORLD_KEY}" "Erstelle map_info.txt..." # KORREKTUR: Die komplizierte Fallback-Logik wird durch den einfachen, funktionierenden vipsheader-Befehl ersetzt. IMG_WIDTH=$(vipsheader -f width "$RAW_MAP_ABSOLUTE_PATH" 2>/dev/null) IMG_HEIGHT=$(vipsheader -f height "$RAW_MAP_ABSOLUTE_PATH" 2>/dev/null) EXTENT_COMMAND="'${MINETESTMAPPER_PATH}' -i '${CURRENT_MINETEST_WORLD_DATA_PATH}' --extent ${MM_ALL_OPTIONS_STR}" MAP_EXTENT=$(eval "${EXTENT_COMMAND}" 2>/dev/null | sed 's/Map extent: //') if [[ "$IMG_WIDTH" =~ ^[0-9]+$ ]] && [ "$IMG_WIDTH" -gt 0 ] && [ -n "$MAP_EXTENT" ]; then MAP_DIMENSIONS="${IMG_WIDTH}x${IMG_HEIGHT}" { echo "map_dimension=${MAP_DIMENSIONS}"; echo "map_extent=${MAP_EXTENT}"; } > "$MAP_INFO_FILE_ABSOLUTE_PATH" log_message "${WORLD_KEY}" "map_info.txt erstellt: Dim=${MAP_DIMENSIONS}, Extent=${MAP_EXTENT}" else log_message "${WORLD_KEY}" "FEHLER: map_info.txt konnte nicht erstellt werden." log_message "${WORLD_KEY}" "-> Ermittelte Bild-Breite: '${IMG_WIDTH}'" log_message "${WORLD_KEY}" "-> Ermittelter Karten-Extent: '${MAP_EXTENT}'" fi awk ' /Unknown nodes:/ {b=1;next} b&&NF==0 {b=0} b&&!/^[ \t]/ {b=0} b{n=$0;sub(/^[ \t]+/,"",n);sub(/[ \t]+$/,"",n);if(n~/:/&&n!="")print n} ' "$MAPPER_RUN_OUTPUT_CAPTURE_FILE" > "${UNKNOWN_NODES_FILE_ABSOLUTE_PATH}.new" if [ -s "${UNKNOWN_NODES_FILE_ABSOLUTE_PATH}.new" ]; then cat "${UNKNOWN_NODES_FILE_ABSOLUTE_PATH}.new" >> "${UNKNOWN_NODES_FILE_ABSOLUTE_PATH}" sort -u "${UNKNOWN_NODES_FILE_ABSOLUTE_PATH}" -o "${UNKNOWN_NODES_FILE_ABSOLUTE_PATH}" fi rm -f "${UNKNOWN_NODES_FILE_ABSOLUTE_PATH}.new" "$MAPPER_RUN_OUTPUT_CAPTURE_FILE" log_message "${WORLD_KEY}" "Erzeuge Web-Version von map.png (max ${RESIZED_MAX_DIMENSION}px) mit 'vips'..." mkdir -p "$(dirname "$WEB_MAP_PNG_FULL_PATH")" (set -o pipefail; vips thumbnail "$RAW_MAP_ABSOLUTE_PATH" "$WEB_MAP_PNG_FULL_PATH" "${RESIZED_MAX_DIMENSION}" --height "${RESIZED_MAX_DIMENSION}" --size "down" 2>&1 | tee -a "$LOG_FILE") log_message "${WORLD_KEY}" "Generiere Kacheln (Zoom: ${GDAL2TILES_ZOOM_LEVELS})..." TEMP_TILES_DIR="${TILES_FULL_OUTPUT_PATH}_temp_$(date +%s)"; (set -o pipefail; gdal2tiles.py --profile=raster --xyz --zoom="${GDAL2TILES_ZOOM_LEVELS}" -r near "${RAW_MAP_ABSOLUTE_PATH}" "${TEMP_TILES_DIR}" 2>&1 | tee -a "$LOG_FILE") if [ $? -ne 0 ]; then log_message "${WORLD_KEY}" "FEHLER: gdal2tiles.py fehlgeschlagen."; rm -rf "$TEMP_TILES_DIR"; exit 1; fi rm -rf "$TILES_FULL_OUTPUT_PATH" mv "$TEMP_TILES_DIR" "$TILES_FULL_OUTPUT_PATH" log_message "${WORLD_KEY}" "Kacheln erfolgreich generiert." prune_archives ARCHIVE_YEAR=$(date '+%Y'); ARCHIVE_MONTH=$(date '+%m'); ARCHIVE_DAY=$(date '+%d') ARCHIVE_DAILY_TARGET_DIR="${ARCHIVE_BASE_WEB_PATH}/${ARCHIVE_YEAR}/${ARCHIVE_MONTH}" ARCHIVE_DAILY_FILE_PATH="${ARCHIVE_DAILY_TARGET_DIR}/${ARCHIVE_DAY}.png" if [ ! -f "$ARCHIVE_DAILY_FILE_PATH" ]; then mkdir -p "$ARCHIVE_DAILY_TARGET_DIR" vips thumbnail "$RAW_MAP_ABSOLUTE_PATH" "$ARCHIVE_DAILY_FILE_PATH" "${RESIZED_MAX_DIMENSION}" --height "${RESIZED_MAX_DIMENSION}" --size "down" fi log_message "${WORLD_KEY}" "Kopiere finale Info-Dateien ins Web-Verzeichnis..." mkdir -p "$WEB_CURRENT_WORLD_DIR" echo "$(date '+%Y-%m-%d %H:%M:%S %Z')" > "${WEB_CURRENT_WORLD_DIR}/last_update.txt" [ -f "$UNKNOWN_NODES_FILE_ABSOLUTE_PATH" ] && cp "$UNKNOWN_NODES_FILE_ABSOLUTE_PATH" "${WEB_CURRENT_WORLD_DIR}/unknown_nodes.txt" [ -f "$MAP_INFO_FILE_ABSOLUTE_PATH" ] && cp "$MAP_INFO_FILE_ABSOLUTE_PATH" "${WEB_CURRENT_WORLD_DIR}/map_info.txt" exit 0