diff --git a/site_generator/functions/generators/world_detail_generator.sh b/site_generator/functions/generators/world_detail_generator.sh index f210d2a..b54c8a9 100644 --- a/site_generator/functions/generators/world_detail_generator.sh +++ b/site_generator/functions/generators/world_detail_generator.sh @@ -172,7 +172,6 @@ generate_world_detail_page() { "web_tiles_rel_path" "$web_tiles_rel_path" \ "web_map_info_rel_path" "${WEB_MAPS_BASE_SUBDIR}/${current_world_key}/map_info.txt" \ "web_areas_json_rel_path" "${WEB_MAPS_BASE_SUBDIR}/${current_world_key}/areas.json" \ - "web_tpads_txt_rel_path" "${WEB_MAPS_BASE_SUBDIR}/${current_world_key}/tpads.txt" \ "map_background_color" "$map_background_color" \ "DEFAULT_PLAYER_SKIN_URL" "$DEFAULT_PLAYER_SKIN_URL" local WORLD_RADAR_HTML; WORLD_RADAR_HTML=$(<"$temp_radar_file") diff --git a/site_generator/templates/css.template b/site_generator/templates/css.template index c322bb2..cc2624e 100755 --- a/site_generator/templates/css.template +++ b/site_generator/templates/css.template @@ -202,7 +202,7 @@ a.read-more-link { font-size: 0.9em; font-weight: bold; } .scroll-to-top-btn { display: none; position: fixed; bottom: 20px; right: 20px; z-index: 999; background-color: #555; color: #FFA500; border: 1px solid #666; border-radius: 50%; width: 40px; height: 40px; text-align: center; font-size: 24px; line-height: 38px; cursor: pointer; transition: background-color 0.3s, opacity 0.3s; } .scroll-to-top-btn:hover { background-color: #666; text-decoration: none; } .scroll-to-top-btn.show { display: block; } -/* Stile für OpenLayers Popups */ +/* Stile für OpenLayers Popups (Leaflet-Look-and-Feel) */ .ol-popup { position: absolute; background-color: #303030; box-shadow: 0 1px 4px rgba(0,0,0,0.4); padding: 1px; border-radius: 5px; bottom: 12px; left: -50px; min-width: 280px; border: 1px solid #555; color: #ddd; } .ol-popup:after, .ol-popup:before { top: 100%; border: solid transparent; content: " "; height: 0; width: 0; position: absolute; pointer-events: none; } .ol-popup:after { border-top-color: #303030; border-width: 10px; left: 48px; margin-left: -10px; } @@ -236,33 +236,36 @@ a.read-more-link { font-size: 0.9em; font-weight: bold; } .admin-box { flex-basis: 100%; } } -/* Stile für OpenLayers Layer Switcher (VOM BENUTZER KORRIGIERT) */ +/* KORREKTUR: Stile für OpenLayers Layer Switcher */ + .layer-switcher.shown { background-color: #303030 !important; } + .layer-switcher button { font-size: 1.2em !important; - width: 40px !important; - height: 40px !important; } + .layer-switcher button:hover, .layer-switcher button:focus { background-color: #666 !important; } + .layer-switcher .panel { background-color: #303030 !important; border: 1px solid #555 !important; color: #ddd !important; margin: 0; - padding: 5px 10px; } + .layer-switcher ul { padding-left: 1em; } + .layer-switcher label { color: #ddd !important; } -/* Styling für Checkboxen im Layer-Switcher */ +/* NEU: Styling für Checkboxen im Layer-Switcher */ .layer-switcher input[type=checkbox], .layer-switcher input[type=radio] { accent-color: #FFA500; @@ -270,7 +273,7 @@ a.read-more-link { font-size: 0.9em; font-weight: bold; } margin-right: 5px; } -/* Vergrößerung der Standard-Karten-Buttons */ +/* NEU: Vergrößerung der Standard-Karten-Buttons */ .ol-zoom .ol-zoom-in, .ol-zoom .ol-zoom-out, .ol-rotate .ol-rotate-reset, @@ -281,17 +284,3 @@ a.read-more-link { font-size: 0.9em; font-weight: bold; } line-height: 1; padding: 0; } - -/* Styling für die Koordinatenanzeige */ -.ol-mouse-position { - top: auto !important; - right: auto !important; - bottom: 8px !important; - left: 8px !important; -} -.custom-mouse-position { - color: #ffffff; - font-family: monospace; - font-size: 0.9em; - text-shadow: 1px 1px 2px #000, -1px -1px 2px #000, 1px -1px 2px #000, -1px 1px 2px #000; -} diff --git a/site_generator/templates/world_detail_radar.template b/site_generator/templates/world_detail_radar.template index 0e5cddf..7e0bf97 100644 --- a/site_generator/templates/world_detail_radar.template +++ b/site_generator/templates/world_detail_radar.template @@ -28,7 +28,6 @@ window.olMap_%%current_world_key%% = null; window.playerMarkerSource_%%current_world_key%% = new ol.source.Vector(); window.parentAreaLayerSource_%%current_world_key%% = new ol.source.Vector(); window.subAreaLayerSource_%%current_world_key%% = new ol.source.Vector(); -window.tpadMarkerSource_%%current_world_key%% = new ol.source.Vector(); window.mapData_%%current_world_key%% = {}; // Konvertiert Minetest-Koordinaten in OpenLayers-Pixel-Koordinaten @@ -42,23 +41,6 @@ function convertMinetestToOpenLayers_%%current_world_key%%(posX, posZ) { return [pixelX, pixelY]; } -// Konvertiert OpenLayers-Pixel-Koordinaten zurück in Minetest-Koordinaten -function convertOpenLayersToMinetest_%%current_world_key%%(coordinate) { - const mapData = window.mapData_%%current_world_key%%; - if (!mapData || !mapData.mapWidth || !coordinate) return ''; - - const pixelX = coordinate[0]; - const pixelY = coordinate[1]; - - const percentX = pixelX / mapData.mapWidth; - const percentZ = -pixelY / mapData.mapHeight; - - const posX = Math.round((percentX * mapData.extentWidth) + mapData.minX); - const posZ = Math.round((percentZ * mapData.extentHeight) + mapData.minZ); - - return `X: ${posX}, Z: ${posZ}`; -} - function createPolygonFromPos_%%current_world_key%%(pos1, pos2) { const p1 = convertMinetestToOpenLayers_%%current_world_key%%(pos1.x, pos1.z); const p2 = convertMinetestToOpenLayers_%%current_world_key%%(pos2.x, pos2.z); @@ -76,13 +58,11 @@ document.addEventListener('DOMContentLoaded', function() { const mapContainer = document.getElementById('ol-map-container-%%current_world_key%%'); const mapInfoPath = '/%%web_map_info_rel_path%%?v=' + new Date().getTime(); const areasPath = '/%%web_areas_json_rel_path%%?v=' + new Date().getTime(); - const tpadsPath = '/%%web_tpads_txt_rel_path%%?v=' + new Date().getTime(); Promise.all([ fetch(mapInfoPath).then(res => { if (!res.ok) throw new Error('map_info.txt'); return res.text(); }), - fetch(areasPath).then(res => { if (!res.ok) return {}; return res.json(); }), - fetch(tpadsPath).then(res => { if (!res.ok) return []; return res.json(); }) - ]).then(([mapInfoText, areaData, tpadData]) => { + fetch(areasPath).then(res => { if (!res.ok) return {}; return res.json(); }) + ]).then(([mapInfoText, areaData]) => { const mapInfo = (text => { const data = {}; @@ -143,22 +123,15 @@ document.addEventListener('DOMContentLoaded', function() { const parentAreaStyle = new ol.style.Style({ stroke: new ol.style.Stroke({ color: 'rgba(0, 100, 255, 0.8)', width: 2 }), fill: new ol.style.Fill({ color: 'rgba(0, 100, 255, 0.2)' }) }); const subAreaStyle = new ol.style.Style({ stroke: new ol.style.Stroke({ color: 'rgba(0, 100, 255, 0.8)', width: 1.5, lineDash: [6, 6] }), fill: new ol.style.Fill({ color: 'transparent' }) }); - const tpadStyle = new ol.style.Style({ - image: new ol.style.Icon({ - anchor: [0.5, 1], anchorXUnits: 'fraction', anchorYUnits: 'fraction', - src: 'data:image/svg+xml;utf8,', - scale: 1.5 - }) - }); - + const playerLayer = new ol.layer.Vector({ source: window.playerMarkerSource_%%current_world_key%%, style: f => f.get('style'), title: 'Spieler' }); - const tpadLayer = new ol.layer.Vector({ source: window.tpadMarkerSource_%%current_world_key%%, style: tpadStyle, title: 'Teleporter' }); - const parentAreaLayer = new ol.layer.Vector({ source: window.parentAreaLayerSource_%%current_world_key%%, style: parentAreaStyle, title: 'Grundstücke', visible: false }); + const parentAreaLayer = new ol.layer.Vector({ source: window.parentAreaLayerSource_%%current_world_key%%, style: parentAreaStyle, title: 'Grundstücke' }); + // KORREKTUR: Die 'title'-Eigenschaft wurde von diesem Layer entfernt const subAreaLayer = new ol.layer.Vector({ source: window.subAreaLayerSource_%%current_world_key%%, style: subAreaStyle, visible: false }); - const overlayLayers = new ol.layer.Group({ title: 'Overlays', layers: [subAreaLayer, parentAreaLayer, tpadLayer, playerLayer] }); + const overlayLayers = new ol.layer.Group({ title: 'Overlays', layers: [subAreaLayer, parentAreaLayer, playerLayer] }); - const view = new ol.View({ projection: projection, center: ol.extent.getCenter(extent), resolutions: viewResolutions, maxZoom: viewResolutions.length - 1 }); + const view = new ol.View({ projection: projection, center: ol.extent.getCenter(extent), resolutions: viewResolutions }); const popupContainer = document.getElementById('popup-%%current_world_key%%'); const popupContent = document.getElementById('popup-content-%%current_world_key%%'); @@ -166,15 +139,7 @@ document.addEventListener('DOMContentLoaded', function() { const overlay = new ol.Overlay({ element: popupContainer, autoPan: { animation: { duration: 250 } } }); popupCloser.onclick = () => { overlay.setPosition(undefined); popupCloser.blur(); return false; }; - const mousePositionControl = new ol.control.MousePosition({ - className: 'custom-mouse-position', - coordinateFormat: function(coordinate) { - return convertOpenLayersToMinetest_%%current_world_key%%(coordinate); - }, - undefinedHTML: ' ' - }); - - const mapControls = [ new ol.control.Zoom(), new ol.control.Rotate(), new ol.control.Attribution({ collapsible: false }), new ol.control.FullScreen(), new ol.control.LayerSwitcher(), mousePositionControl ]; + const mapControls = [ new ol.control.Zoom(), new ol.control.Rotate(), new ol.control.Attribution({ collapsible: false }), new ol.control.FullScreen(), new ol.control.LayerSwitcher() ]; window.olMap_%%current_world_key%% = new ol.Map({ controls: mapControls, @@ -195,14 +160,6 @@ document.addEventListener('DOMContentLoaded', function() { window.parentAreaLayerSource_%%current_world_key%%.addFeature(feature); } } - - tpadData.forEach(tpad => { - const coords = convertMinetestToOpenLayers_%%current_world_key%%(tpad.posX, tpad.posZ); - if (coords) { - const feature = new ol.Feature({ geometry: new ol.geom.Point(coords), tpadData: tpad }); - window.tpadMarkerSource_%%current_world_key%%.addFeature(feature); - } - }); map.on('click', function(evt) { overlay.setPosition(undefined); @@ -216,16 +173,17 @@ document.addEventListener('DOMContentLoaded', function() { if (playerData) { const statusDotClass = playerFeature.get('statusDotClass'); const lastLoginFormatted = playerFeature.get('lastLoginFormatted'); - // KORREKTUR: Layout des Spieler-Popups const popupHTML = `
-
-
- ${playerData.name}
-
-
+
+
+ ${playerData.name}
+
+
- Parzellen:
Parzellen:
- Karte konnte nicht initialisiert werden: " + error.message + "
"; }); diff --git a/sync_tpads.sh b/sync_tpads.sh deleted file mode 100755 index d04af10..0000000 --- a/sync_tpads.sh +++ /dev/null @@ -1,117 +0,0 @@ -#!/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}' ---" - 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}/" - -# === 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 Quell- und Zieldateien -MOD_STORAGE_DB_PATH="${CURRENT_MINETEST_WORLD_DATA_PATH}mod_storage.sqlite" -TPADS_JSON_TARGET_DIR="${WEB_ROOT_PATH}/${WEB_MAPS_BASE_SUBDIR}/${WORLD_KEY}" -TPADS_JSON_FILE_PATH="${TPADS_JSON_TARGET_DIR}/tpads.txt" -TPADS_JSON_TMP_FILE_PATH="${TPADS_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. Beende."; exit 1; } -trap 'rm -f "$LOCK_FILE"; log_message "${WORLD_KEY}" "Skript beendet."' EXIT -log_message "${WORLD_KEY}" "Script gestartet." - -if [ ! -f "$MOD_STORAGE_DB_PATH" ]; then - log_message "${WORLD_KEY}" "INFO: mod_storage.sqlite nicht gefunden. Erstelle leere tpads.txt." - mkdir -p "$TPADS_JSON_TARGET_DIR" - echo "[]" > "$TPADS_JSON_FILE_PATH" - exit 0 -fi - -log_message "${WORLD_KEY}" "Lese ${MOD_STORAGE_DB_PATH} und extrahiere öffentliche TPADs (type=4)..." - -# KORREKTUR: Die 'trim'-Funktion wird durch 'sub' ersetzt für höhere Kompatibilität. -$SQLITE_CMD "$MOD_STORAGE_DB_PATH" "SELECT key, value FROM entries WHERE modname = 'tpad' AND INSTR(key, 'pads:') = 1;" | \ -jq -Rs ' - # Definiere eine eigene trimm-Funktion für ältere jq-Versionen - def trim: sub("^\\s+"; "") | sub("\\s+$"; ""); - - split("\n") | - map(select(length > 0)) | - map( - split("|") | {key: .[0], value: .[1]} | - (.key | split(":")[1]) as $owner | - (.value | [capture("\\[\"\\((?