#!/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 config.sh nicht unter ${CONFIG_FILE_PATH} 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}' ---" # Rufe das Skript für die gefundene Welt rekursiv auf 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 # ############################################################################# # Prüfe Abhängigkeiten, bevor irgendetwas anderes passiert /opt/luweb/check_dependencies.sh || exit 1 WORLD_KEY=$1 CURRENT_MINETEST_WORLD_DATA_PATH="${MINETESTMAPPER_WORLD_DATA_BASE_PATH}${WORLD_KEY}/" # === Abgeleitete Variablen === SCRIPT_BASENAME=$(basename "$0" .sh) LOG_FILE="${LOG_DIR_BASE}/${SCRIPT_BASENAME}_${WORLD_KEY}.log" LOCK_FILE="${LOCK_FILE_BASE_DIR}/${SCRIPT_BASENAME}_${WORLD_KEY}.lock" # Pfade zu den Datenbanken und der Zieldatei PLAYERS_DB_PATH="${CURRENT_MINETEST_WORLD_DATA_PATH}players.sqlite" AUTH_DB_PATH="${CURRENT_MINETEST_WORLD_DATA_PATH}auth.sqlite" PLAYERS_JSON_TARGET_DIR="${WEB_ROOT_PATH}/${WEB_MAPS_BASE_SUBDIR}/${WORLD_KEY}" PLAYERS_JSON_FILE_PATH="${PLAYERS_JSON_TARGET_DIR}/players.txt" PLAYERS_JSON_TMP_FILE_PATH="${PLAYERS_JSON_FILE_PATH}.tmp" # SQLite-Befehl SQLITE_CMD="sqlite3 -readonly" # === Hauptlogik für eine einzelne Welt === exec 200>"$LOCK_FILE" flock -n 200 || { log_message "${WORLD_KEY}" "Script ${SCRIPT_BASENAME}.sh ist bereits für diese Welt aktiv (Lock: ${LOCK_FILE}). Beende."; exit 1; } trap 'rm -f "$LOCK_FILE"; log_message "${WORLD_KEY}" "Skript beendet."' EXIT log_message "${WORLD_KEY}" "Script gestartet." if [ ! -f "$PLAYERS_DB_PATH" ] || [ ! -f "$AUTH_DB_PATH" ]; then log_message "${WORLD_KEY}" "FEHLER: players.sqlite oder auth.sqlite nicht gefunden. Pfade prüfen:" log_message "${WORLD_KEY}" "-> ${PLAYERS_DB_PATH}" log_message "${WORLD_KEY}" "-> ${AUTH_DB_PATH}" exit 1 fi echo "{" > "$PLAYERS_JSON_TMP_FILE_PATH" first_entry=true player_list_query="SELECT id, name, last_login FROM auth;" $SQLITE_CMD "$AUTH_DB_PATH" "$player_list_query" | while IFS='|' read -r player_id name last_login; do log_message "${WORLD_KEY}" "Verarbeite Spieler: ${name} (ID: ${player_id})" player_data_query="SELECT posX, posY, posZ, hp, breath, pitch, yaw, creation_date FROM player WHERE name = '${name}';" player_data=$($SQLITE_CMD "$PLAYERS_DB_PATH" "$player_data_query") if [ -z "$player_data" ]; then log_message "${WORLD_KEY}" "WARNUNG: Spieler '${name}' hat keine Positionsdaten in players.sqlite. Wird übersprungen." continue fi IFS='|' read -r posX posY posZ hp breath pitch yaw creation_date <<< "$player_data" posX_rounded=$(printf "%.0f" "$(echo "$posX / 10" | bc)") posY_rounded=$(printf "%.0f" "$(echo "$posY / 10" | bc)") posZ_rounded=$(printf "%.0f" "$(echo "$posZ / 10" | bc)") pitch_rounded=$(printf "%.0f" "$pitch") yaw_rounded=$(printf "%.0f" "$yaw") stamina_query="SELECT value FROM player_metadata WHERE player = '${name}' AND metadata = 'hunger_ng:hunger_value';" stamina_val=$($SQLITE_CMD "$PLAYERS_DB_PATH" "$stamina_query") stamina_rounded=$(printf "%.0f" "${stamina_val:-0}") privs_query="SELECT privilege FROM user_privileges WHERE id = ${player_id};" privs_string=$($SQLITE_CMD "$AUTH_DB_PATH" "$privs_query" | tr '\n' ',' | sed 's/,$//') if [ "$first_entry" = true ]; then first_entry=false else echo "," >> "$PLAYERS_JSON_TMP_FILE_PATH" fi printf ' "%s": {\n' "$player_id" >> "$PLAYERS_JSON_TMP_FILE_PATH" printf ' "name": "%s",\n' "$name" >> "$PLAYERS_JSON_TMP_FILE_PATH" printf ' "pitch": %d,\n' "$pitch_rounded" >> "$PLAYERS_JSON_TMP_FILE_PATH" printf ' "yaw": %d,\n' "$yaw_rounded" >> "$PLAYERS_JSON_TMP_FILE_PATH" printf ' "posX": %d,\n' "$posX_rounded" >> "$PLAYERS_JSON_TMP_FILE_PATH" printf ' "posY": %d,\n' "$posY_rounded" >> "$PLAYERS_JSON_TMP_FILE_PATH" printf ' "posZ": %d,\n' "$posZ_rounded" >> "$PLAYERS_JSON_TMP_FILE_PATH" printf ' "hp": %d,\n' "$hp" >> "$PLAYERS_JSON_TMP_FILE_PATH" printf ' "breath": %d,\n' "$breath" >> "$PLAYERS_JSON_TMP_FILE_PATH" printf ' "stamina": %d,\n' "$stamina_rounded" >> "$PLAYERS_JSON_TMP_FILE_PATH" printf ' "creation_date": "%s",\n' "$(echo "$creation_date" | sed 's/ /T/')Z" >> "$PLAYERS_JSON_TMP_FILE_PATH" printf ' "last_login": %s,\n' "$last_login" >> "$PLAYERS_JSON_TMP_FILE_PATH" printf ' "privilege": "%s"\n' "$privs_string" >> "$PLAYERS_JSON_TMP_FILE_PATH" printf ' }' >> "$PLAYERS_JSON_TMP_FILE_PATH" done echo "" >> "$PLAYERS_JSON_TMP_FILE_PATH" echo "}" >> "$PLAYERS_JSON_TMP_FILE_PATH" mkdir -p "$PLAYERS_JSON_TARGET_DIR" mv "$PLAYERS_JSON_TMP_FILE_PATH" "$PLAYERS_JSON_FILE_PATH" log_message "${WORLD_KEY}" "players.txt erfolgreich synchronisiert nach ${PLAYERS_JSON_FILE_PATH}" exit 0