From 6d9651b4ffabd34549038fc76e06f74a23afe987 Mon Sep 17 00:00:00 2001 From: rainer Date: Sun, 22 Jun 2025 02:50:07 +0200 Subject: [PATCH] Update README.md, colors.txt, config.sh, and 10 more files for alpha v0.3 --- README.md | 107 ++++++----- colors.txt | 167 ++++++++++++++++++ config.sh | 4 +- .../generators/world_detail_generator.sh | 4 +- .../generators/world_overview_generator.sh | 13 +- site_generator/templates/css.template | 14 +- site_generator/templates/html_footer.template | 36 ++++ .../templates/world_detail_page.template | 19 +- .../world_detail_playerlist.template | 40 +++-- .../templates/world_detail_radar.template | 25 ++- .../templates/worlds_overview_entry.template | 2 +- sync_players.sh | 127 +++++++++++++ web_content/images/players/Rage87.png | Bin 0 -> 860 bytes 13 files changed, 449 insertions(+), 109 deletions(-) create mode 100755 sync_players.sh create mode 100755 web_content/images/players/Rage87.png diff --git a/README.md b/README.md index b44d866..f1e0551 100644 --- a/README.md +++ b/README.md @@ -7,47 +7,40 @@ This system is designed from the ground up to be modular, easily configurable, a ## ✨ Features * **Automated Map Generation:** Leverages `minetestmapper` to create high-resolution PNG maps of your game worlds. -* **Performant Image Processing:** Uses `vips`, a high-performance and memory-efficient library, to scale even huge maps (tested up to 64k x 64k pixels) for the web. * **Tiled Map Generation:** Uses `gdal2tiles.py` to create performant, zoomable map tiles for a smooth user experience. -* **Dynamic Map Viewer:** Implements an interactive map viewer using **OpenLayers**, powered by the generated map tiles, including digital zoom beyond the highest resolution. -* **Live Player Tracking:** Dynamically fetches and displays player locations as markers on the live map, including custom-styled popups. +* **Dynamic Map Viewer:** Implements an interactive map viewer using [Leaflet.js](https://leafletjs.com/), powered by the generated map tiles. +* **Live Player Tracking:** Dynamically fetches and displays player locations as markers on the live map. * **Map Archive:** Automatically saves a daily snapshot of the map and makes it available through a dropdown viewer on the world detail page. * **Template-Driven Site Generation:** Builds all static HTML pages from simple, customizable templates. -* **Flexible Configuration:** Configuration is easy with a central global config and a `web.conf` file for every one of your worlds. -* **Automation-Ready:** Designed for unattended execution via scheduling tools like `cron`. +* **Flexible Configuration:** Configuration is easy with a central global config and a sub-config file for every of your worlds. +* **Automation-Ready:** Designed for unattended execution via scheduling tools like cron. ## 🔧 Prerequisites To run this system, the following software packages must be installed on your server: * **bash:** The scripting language used for the entire project. -* **vips:** A high-performance image processing library that replaces `convert` (ImageMagick). - * *Debian/Ubuntu Install:* `sudo apt-get install libvips-tools` -* **ImageMagick:** Currently still required for the `identify` command (to read image dimensions). +* **ImageMagick:** Required for `identify` (to read image dimensions) and `convert` (to resize images). * *Debian/Ubuntu Install:* `sudo apt-get install imagemagick` * **GDAL/OGR:** Provides the `gdal2tiles.py` script for tile generation. * *Debian/Ubuntu Install:* `sudo apt-get install gdal-bin python3-gdal` -* **minetestmapper:** The executable used to render maps from world data. Must be placed within the project directory. -* **iproute2:** Provides the `ss` command for `check_server_status.sh` (usually pre-installed on most systems). +* **minetestmapper:** The executable used to render maps from world data. This must be placed within the project directory. * **Web Server:** A web server like Nginx or Apache is needed to serve the generated static files. -An included script (`check_dependencies.sh`) can automatically verify all important dependencies. - ## ⚙️ Installation & Setup -### 1. Download/Clone Project Files +### 1. Clone the Repository -Download the **latest build** from the [Releases-Page](https://git.geigernet.eu/rainer/luanti-web/releases) and extract it to a base directory on your server, such as `/opt/luweb/`. +Download the **latest build** from the [Releases-Page](https://git.geigernet.eu/rainer/luanti-web/releases) and save it to your server's base directory, such as `/opt/luweb/`. OR -Clone the Git repository to a base directory. +Clone all project files to a base directory on your server. -```bash -git clone [https://git.geigernet.eu/rainer/luanti-web.git](https://git.geigernet.eu/rainer/luanti-web.git) /opt/luweb +``` +git clone https://git.geigernet.eu/rainer/luanti-web.git /opt/luweb cd /opt/luweb -# Make all scripts executable -chmod +x generate_map.sh generate_site.sh check_server_status.sh check_dependencies.sh +chmod +x generate_map.sh generate_site.sh ``` ### 2. Global Configuration @@ -57,45 +50,53 @@ The main configuration file is `config.sh`. You must edit this file to match you **Key variables in `config.sh`:** * `BASE_SCRIPT_DIR`: The root directory of the project (e.g., `/opt/luweb`). -* `MINETESTMAPPER_WORLD_DATA_BASE_PATH`: The path to your Minetest/Luanti worlds' data directory. +* `MINETESTMAPPER_WORLD_DATA_BASE_PATH`: The path to your Minetest/Luanti worlds' data directory (e.g., `/worlds/`), docker compatible. * `WEB_ROOT_PATH`: The document root of your website where the generated files will be placed (e.g., `/var/www/your-domain.com/web`). * `LOG_DIR_BASE`: The directory where log files will be written (e.g., `/var/log/luweb`). ### 3. Per-World Configuration -The system is designed so that **only worlds with a `web.conf` file** will be displayed in the web frontend. This gives you full control over which worlds are publicly visible. +For each world you want to feature on the website, a `web.conf` file must exist within that world's data directory (e.g., `/worlds/my_world/web.conf`). This file allows you to override global defaults with world-specific settings. -To add a world, copy the template `site_generator/examples/web.conf.template` into the data directory of the respective world (e.g., `/worlds/my_world/web.conf`) and adjust the values. +A minimal `web.conf` could look like this and is automatically created for every detected world in your `MINETESTMAPPER_WORLD_DATA_BASE_PATH`: + +```bash +# Display name for the world +WORLD_DISPLAY_NAME="My Creative World" + +# Server connection details +SERVER_ADDRESS="your-server.com" +SERVER_PORT="30001" + +# A short description for the world overview page +WORLD_SHORT_DESCRIPTION="A brief, catchy description of this world." + +# A detailed HTML description for the world's detail page +WORLD_LONG_DESCRIPTION="

A longer description with HTML support.

" +``` ## 📂 Directory Structure -The system now uses a modular structure to improve maintainability: +The system expects the following directory structure: ```md /opt/luweb/ ├── config.sh ├── generate_map.sh ├── generate_site.sh -├── check_server_status.sh -├── check_dependencies.sh ├── minetestmapper (executable) ├── site_generator/ -│ ├── functions/ -│ │ ├── 01_utils.sh -│ │ ├── 02_init.sh -│ │ ├── 03_html_helpers.sh -│ │ └── generators/ -│ │ ├── css_generator.sh -│ │ ├── static_pages_generator.sh -│ │ ├── world_detail_generator.sh -│ │ └── world_overview_generator.sh │ ├── templates/ │ │ ├── world_detail_page.template +│ │ ├── world_detail_archive.template │ │ └── ... │ └── examples/ │ └── web.conf.template ├── web_content/ │ ├── images/ +│ │ └── players/ │ └── static/ +│ ├── startseite_content.html +│ └── ... └── worldmaps_output/ └── / ├── map.png @@ -104,44 +105,38 @@ The system now uses a modular structure to improve maintainability: ## 🚀 Usage -### 1. Map and Data Generation +The scripts are designed to be run from the command line, either manually or via automated jobs. + +### 1. Map and Tile Generation + +The `generate_map.sh` script creates the map, tiles, and archive images for a specific world. It must be called with the "world key" (the name of the world's directory) as an argument. -The `generate_map.sh` script creates the map, tiles, and metadata for a specific world. ```bash -# Generate assets for the world in the 'world' directory +# Generate map assets for the world located in the 'world' directory ./generate_map.sh world ``` ### 2. Website Generation -The `generate_site.sh` script builds the entire website for all worlds that have a `web.conf`. +The `generate_site.sh` script builds the entire website (overview, detail pages, etc.). It scans all configured world directories and creates a detail page for each one. + ```bash # Generate the complete website ./generate_site.sh ``` -### 3. Live Status Check +### 3. Automation (Cronjob) -The `check_server_status.sh` script checks the online status of all configured worlds. It should be run very frequently. -```bash -# Check the server status -./check_server_status.sh -``` - -### 4. Automation (Cronjob) - -Setting up cronjobs is recommended for fully automatic operation. +For fully automatic operation, setting up cronjobs is recommended. **Example for `crontab -e`:** + ```bash -# Generate map assets for 'world' once per hour -0 * * * * /opt/luweb/generate_map.sh world >> /var/log/luweb/cron.log 2>&1 +# Generate map assets for the 'world' every 30 minutes +*/30 * * * * /opt/luweb/generate_map.sh world >> /var/log/luweb/cron.log 2>&1 -# Check the server status every 2 minutes -*/2 * * * * /opt/luweb/check_server_status.sh >> /var/log/luweb/cron.log 2>&1 - -# Re-build the website (e.g., for new admins, changed descriptions) twice a day -0 */12 * * * /opt/luweb/generate_site.sh >> /var/log/luweb/cron.log 2>&1 +# Re-build the website every 12 hours +* */12 * * * /opt/luweb/generate_site.sh >> /var/log/luweb/cron.log 2>&1 ``` ## 📄 License @@ -167,4 +162,4 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ## 👤 Authors -* **Rage87** (Main-Developer) +* **Rage87** (Main-Developer) \ No newline at end of file diff --git a/colors.txt b/colors.txt index cdb7ada..fc25ca0 100644 --- a/colors.txt +++ b/colors.txt @@ -1235,3 +1235,170 @@ xpanes:pane 249 249 249 64 16 xpanes:pane_flat 249 249 249 64 16 xpanes:trapdoor_steel_bar 127 127 127 64 16 xpanes:trapdoor_steel_bar_open 77 77 77 64 16 + +# === NEUE EINTRÄGE VOM 21.06.2025 === + +# default +default:fence_aspen_wood_end 210 199 170 # Entspricht default:aspen_wood + +# fake_fire +fake_fire:chimney_sandstone 198 193 143 # Entspricht default:sandstone + +# farming +farming:barley_8 218 190 70 # Reifes Getreide, gold-gelb +farming:cabbage_5 135 240 80 # Reifender Kohl, hellgrün +farming:carrot_8 255 140 0 # Reife Karotte, kräftiges Orange +farming:cocoa_1 100 150 50 # Junge Kakaopflanze, grün +farming:corn_8 255 215 0 # Reifer Mais, goldgelb +farming:eggplant_4 75 0 130 # Reife Aubergine, tiefes Lila +farming:grapes_5 100 0 100 # Reifende Weintraube, lila +farming:grapes_6 100 0 100 # Reifende Weintraube, lila +farming:grapes_7 100 0 100 # Reifende Weintraube, lila +farming:grapes_8 100 0 100 # Reife Weintraube, lila +farming:hemp_8 60 100 50 # Reife Hanfpflanze, dunkles Grün +farming:onion_3 220 230 200 # Junge Zwiebel, hellgrün/weiß +farming:onion_4 245 245 230 # Reifende Zwiebel, fast weiß +farming:pea_1 100 160 80 # Erbsenpflanze, junges Grün +farming:pea_2 90 150 70 # Erbsenpflanze, mittleres Grün +farming:pea_3 80 140 60 # Erbsenpflanze, sattes Grün +farming:pepper_5 220 100 30 # Reifende Paprika, orange-rot +farming:pepper_7 200 30 30 # Reife Paprika, rot +farming:rhubarb_1 80 140 70 # Rhabarber, jung, grünlich +farming:rhubarb_2 150 80 80 # Rhabarber, reifend, rötlich +farming:rice_8 230 220 180 # Reifer Reis, helles Stroh +farming:rye_7 200 170 80 # Reifender Roggen +farming:rye_8 210 180 75 # Reifer Roggen, goldbraun +farming:sunflower_4 80 150 40 # Sonnenblume, mittlere Wachstumsphase + +# irrigation +irrigation:water_barrel 131 102 57 # Holzfass, entspricht default:wood + +# markers +markers:mark 128 128 128 # Generischer Marker, neutralgrau + +# mesecons_lightstone +mesecons_lightstone:lightstone_blue_on 80 80 255 # Leuchtendes Blau +mesecons_lightstone:lightstone_green_off 0 80 0 # Dunkles Grün (aus) +mesecons_lightstone:lightstone_green_on 0 255 0 # Leuchtendes Grün +mesecons_lightstone:lightstone_red_off 80 0 0 # Dunkles Rot (aus) +mesecons_lightstone:lightstone_red_on 255 0 0 # Leuchtendes Rot +mesecons_lightstone:lightstone_violet_off 80 0 80 # Dunkles Violett (aus) +mesecons_lightstone:lightstone_violet_on 200 0 200 # Leuchtendes Violett +mesecons_lightstone:lightstone_yellow_on 255 255 0 # Leuchtendes Gelb + +# mesecons_switch +mesecons_switch:mesecon_switch_off 110 110 110 # Schaltergehäuse, steingrau + +# mesecons +mesecons:wire_00000000_off 139 50 50 # Mesecon-Draht im Aus-Zustand (dunkles Kupfer) +mesecons:wire_00010000_off 139 50 50 +mesecons:wire_00010000_on 255 200 0 # Mesecon-Draht im An-Zustand (leuchtend) +mesecons:wire_00010001_off 139 50 50 +mesecons:wire_00100000_off 139 50 50 +mesecons:wire_00100000_on 255 200 0 +mesecons:wire_00100010_off 139 50 50 +mesecons:wire_00100010_on 255 200 0 +mesecons:wire_00110000_off 139 50 50 +mesecons:wire_00110000_on 255 200 0 +mesecons:wire_00110001_off 139 50 50 +mesecons:wire_00110001_on 255 200 0 +mesecons:wire_00110011_off 139 50 50 +mesecons:wire_00110011_on 255 200 0 +mesecons:wire_01000000_on 255 200 0 +mesecons:wire_01000100_on 255 200 0 +mesecons:wire_01010000_off 139 50 50 +mesecons:wire_01010000_on 255 200 0 +mesecons:wire_01010001_on 255 200 0 +mesecons:wire_01010100_on 255 200 0 +mesecons:wire_01010101_off 139 50 50 +mesecons:wire_01010101_on 255 200 0 +mesecons:wire_01100000_off 139 50 50 +mesecons:wire_01100000_on 255 200 0 +mesecons:wire_01100100_off 139 50 50 +mesecons:wire_01100100_on 255 200 0 +mesecons:wire_01100110_off 139 50 50 +mesecons:wire_01100110_on 255 200 0 +mesecons:wire_01110000_off 139 50 50 +mesecons:wire_01110000_on 255 200 0 +mesecons:wire_01110001_off 139 50 50 +mesecons:wire_01110011_on 255 200 0 +mesecons:wire_01110100_off 139 50 50 +mesecons:wire_01110100_on 255 200 0 +mesecons:wire_01110101_off 139 50 50 +mesecons:wire_01110101_on 255 200 0 +mesecons:wire_01110110_off 139 50 50 +mesecons:wire_01110111_off 139 50 50 +mesecons:wire_01110111_on 255 200 0 +mesecons:wire_10000000_off 139 50 50 +mesecons:wire_10000000_on 255 200 0 +mesecons:wire_10001000_on 255 200 0 +mesecons:wire_10010000_off 139 50 50 +mesecons:wire_10010000_on 255 200 0 +mesecons:wire_10011000_on 255 200 0 +mesecons:wire_10011001_off 139 50 50 +mesecons:wire_10011001_on 255 200 0 +mesecons:wire_10100000_on 255 200 0 +mesecons:wire_10100010_off 139 50 50 +mesecons:wire_10100010_on 255 200 0 +mesecons:wire_10101000_on 255 200 0 +mesecons:wire_10101010_off 139 50 50 +mesecons:wire_10101010_on 255 200 0 +mesecons:wire_10110010_on 255 200 0 +mesecons:wire_10111000_on 255 200 0 +mesecons:wire_10111001_off 139 50 50 +mesecons:wire_10111011_off 139 50 50 +mesecons:wire_10111011_on 255 200 0 +mesecons:wire_11000000_off 139 50 50 +mesecons:wire_11001100_off 139 50 50 +mesecons:wire_11001100_on 255 200 0 +mesecons:wire_11010000_on 255 200 0 +mesecons:wire_11010100_off 139 50 50 +mesecons:wire_11011000_off 139 50 50 +mesecons:wire_11011001_off 139 50 50 +mesecons:wire_11011001_on 255 200 0 +mesecons:wire_11011100_off 139 50 50 +mesecons:wire_11011100_on 255 200 0 +mesecons:wire_11011101_on 255 200 0 +mesecons:wire_11100000_off 139 50 50 +mesecons:wire_11100000_on 255 200 0 +mesecons:wire_11100010_off 139 50 50 +mesecons:wire_11100110_on 255 200 0 +mesecons:wire_11101000_on 255 200 0 +mesecons:wire_11101010_off 139 50 50 +mesecons:wire_11101010_on 255 200 0 +mesecons:wire_11101100_on 255 200 0 +mesecons:wire_11101110_on 255 200 0 +mesecons:wire_11110000_off 139 50 50 +mesecons:wire_11110000_on 255 200 0 +mesecons:wire_11110010_off 139 50 50 +mesecons:wire_11110011_on 255 200 0 +mesecons:wire_11110100_off 139 50 50 +mesecons:wire_11110101_off 139 50 50 +mesecons:wire_11110101_on 255 200 0 +mesecons:wire_11110110_on 255 200 0 +mesecons:wire_11110111_on 255 200 0 +mesecons:wire_11111000_off 139 50 50 +mesecons:wire_11111000_on 255 200 0 +mesecons:wire_11111001_off 139 50 50 +mesecons:wire_11111011_on 255 200 0 +mesecons:wire_11111100_off 139 50 50 +mesecons:wire_11111100_on 255 200 0 +mesecons:wire_11111101_on 255 200 0 +mesecons:wire_11111110_on 255 200 0 +mesecons:wire_11111111_off 139 50 50 +mesecons:wire_11111111_on 255 200 0 + +# moreblocks +moreblocks:slab_coal_stone_quarter 60 60 60 # Entspricht moreblocks:coal_stone_bricks +moreblocks:slab_stone_block 100 97 96 # Entspricht default:stone_block + +# morelights_vintage +morelights_vintage:lantern_f 70 70 70 # Laternengehäuse, dunkles Metall + +# mystreets +mystreets:ramp_asphalt_center_solid_long 55 55 60 # Entspricht mystreets:asphalt +mystreets:ramp_asphalt_long 55 55 60 # Entspricht mystreets:asphalt +mystreets:ramp_asphalt_side_solid_left_long 55 55 60 # Entspricht mystreets:asphalt +mystreets:ramp_asphalt_side_solid_right_long 55 55 60 # Entspricht mystreets:asphalt +mystreets:ramp_sidewalk_long 150 150 150 # Entspricht mystreets:sidewalk +mystreets:stop_sign 200 0 0 # Stoppschild-Rot diff --git a/config.sh b/config.sh index 5e330c4..9d009a4 100755 --- a/config.sh +++ b/config.sh @@ -17,8 +17,8 @@ MINETESTMAPPER_WORLD_DATA_BASE_PATH="/opt/luanti/data/worlds/" DEFAULT_MM_OPT_ZOOM_LEVEL="2"; DEFAULT_MM_OPT_MIN_Y="-25" DEFAULT_MM_OPT_ORIGINCOLOR="#ff0000"; DEFAULT_MM_OPT_PLAYERCOLOR="#ff0000" DEFAULT_MM_OPT_SCALECOLOR="#ff0000"; DEFAULT_MM_OPT_BGCOLOR="#dddddd" -DEFAULT_MM_CFG_DRAWALPHA="true"; DEFAULT_MM_CFG_DRAWORIGIN="true" -DEFAULT_MM_CFG_DRAWPLAYERS="true"; DEFAULT_MM_CFG_DRAWSCALE="true" +DEFAULT_MM_CFG_DRAWALPHA="true"; DEFAULT_MM_CFG_DRAWORIGIN="false" +DEFAULT_MM_CFG_DRAWPLAYERS="false"; DEFAULT_MM_CFG_DRAWSCALE="false" # --- Dateinamen und relative Pfade (innerhalb BASE_SCRIPT_DIR) --- RAW_MAP_BASE_SUBDIR="worldmaps_output" diff --git a/site_generator/functions/generators/world_detail_generator.sh b/site_generator/functions/generators/world_detail_generator.sh index 3e07154..9489634 100644 --- a/site_generator/functions/generators/world_detail_generator.sh +++ b/site_generator/functions/generators/world_detail_generator.sh @@ -31,7 +31,6 @@ generate_world_detail_page() { local tiles_subdir_name; tiles_subdir_name=$(get_config_value_from_file "$web_conf_file" "TILES_SUBDIR_NAME" "$DEFAULT_TILES_SUBDIR_NAME") local web_tiles_rel_path="${WEB_MAPS_BASE_SUBDIR}/${current_world_key}/${tiles_subdir_name}" - # HINZUGEFÜGT: Hintergrundfarbe auslesen local map_background_color; map_background_color=$(get_config_value_from_file "$web_conf_file" "MM_OPT_BGCOLOR" "$DEFAULT_MM_OPT_BGCOLOR") local WORLD_DISPLAY_NAME_PAGE; WORLD_DISPLAY_NAME_PAGE=$(get_config_value_from_file "$web_conf_file" "WORLD_DISPLAY_NAME") @@ -173,7 +172,8 @@ generate_world_detail_page() { "RESOLUTIONS_JS_ARRAY" "$resolutions_array" \ "web_tiles_rel_path" "$web_tiles_rel_path" \ "web_map_info_rel_path" "${WEB_MAPS_BASE_SUBDIR}/${current_world_key}/map_info.txt" \ - "map_background_color" "$map_background_color" + "map_background_color" "$map_background_color" \ + "DEFAULT_PLAYER_SKIN_URL" "$DEFAULT_PLAYER_SKIN_URL" local WORLD_RADAR_HTML; WORLD_RADAR_HTML=$(<"$temp_radar_file") rm "$temp_radar_file" diff --git a/site_generator/functions/generators/world_overview_generator.sh b/site_generator/functions/generators/world_overview_generator.sh index dcbad28..27a77ae 100644 --- a/site_generator/functions/generators/world_overview_generator.sh +++ b/site_generator/functions/generators/world_overview_generator.sh @@ -49,13 +49,9 @@ generate_worlds_overview() { local detail_page_filename="world_${current_world_key}.html" local preview_img_rel_path="${WEB_MAPS_BASE_SUBDIR}/${current_world_key}/${current_map_png_filename_for_preview_ov}" local preview_img_abs_path="${WEB_ROOT_PATH}/${preview_img_rel_path}" - local online_status_text="offline"; local online_status_class="offline" - local status_file_for_overview="${WEB_ROOT_PATH}/${WEB_MAPS_BASE_SUBDIR}/${current_world_key}/online_status.txt" - if [ -f "$status_file_for_overview" ]; then - local status_line_overview - status_line_overview=$(cat "$status_file_for_overview") - if [[ "$status_line_overview" == "online"* ]]; then online_status_text="online"; online_status_class="online"; fi - fi + + # KORREKTUR: Logik zur Status-Prüfung entfernt. Stattdessen wird nur der Pfad zur Status-Datei erstellt. + local status_url_path="/${WEB_MAPS_BASE_SUBDIR}/${current_world_key}/online_status.txt" local preview_img_html if [ -f "$preview_img_abs_path" ]; then @@ -70,8 +66,7 @@ generate_worlds_overview() { "detail_page_filename" "$detail_page_filename" \ "preview_img_html" "$preview_img_html" \ "world_display_name_ov" "$world_display_name_ov" \ - "online_status_class" "$online_status_class" \ - "online_status_text" "$online_status_text" \ + "status_url" "$status_url_path" \ "admin_name_ov" "$admin_name_ov" \ "world_short_desc_ov" "$world_short_desc_ov" cat "$temp_overview_entry_file" >> "$overview_file" diff --git a/site_generator/templates/css.template b/site_generator/templates/css.template index 40c70af..d852780 100755 --- a/site_generator/templates/css.template +++ b/site_generator/templates/css.template @@ -94,13 +94,14 @@ a.world-preview:hover { .world-preview-text p { color: #ccc !important; margin-top: 0; text-decoration: none !important; } .world-preview-footer { font-size: 0.85em; color: #aaa; border-top: 1px solid #666; margin-top: 10px; padding-top: 10px; } .online-status-badge { font-size: 0.8em; padding: 3px 8px; border-radius: 10px; color: white !important; margin-left: 10px; } -.online-status-badge.online { background-color: #3E8E41; } .online-status-badge.offline { background-color: #C0392B; } +.online-status-badge.online { background-color: #3E8E41; } +.online-status-badge.offline { background-color: #C0392B; } +.online-status-badge.unknown { background-color: #808080; } /* HINZUGEFÜGT */ /* Welt-Detailseite */ .page-nav-buttons { text-align: right; margin-top: 10px; margin-bottom: 15px; } .page-nav-buttons .button { background-color: #555; color: #FFA500; border: 1px solid #666; padding: 5px 10px; margin-left: 5px; margin-bottom: 5px; border-radius: 3px; text-decoration: none; font-size: 0.9em; display: inline-block; } .page-nav-buttons .button:hover { background-color: #666; color: #FFC500; } -/* KORREKTUR: Klasse umbenannt von .leaflet-map zu .map-view-container */ .map-view-container { width: 100%; height: 600px; @@ -467,10 +468,9 @@ a.read-more-link { margin: 5px; width: auto; } - .player-box { width: calc((100% - 15px) / 2); } /* 2 Spalten auf Tablets */ + .player-box { width: calc((100% - 15px) / 2); } .page-nav-buttons .button { margin-top: 5px;} - /* Responsive Stile für Welt-Vorschau */ .world-preview .world-preview-content { flex-direction: column; } @@ -481,15 +481,15 @@ a.read-more-link { margin-right: 0; margin-bottom: 15px; } - .map-view-container { /* KORREKTUR: Klasse umbenannt */ + .map-view-container { height: auto; aspect-ratio: 4 / 3; max-height: 70vh; } } @media (max-width: 480px) { - .player-box { width: 100%; } /* 1 Spalte auf Handys */ - .admin-box { flex-basis: 100%; } /* 1 Spalte auf Handys */ + .player-box { width: 100%; } + .admin-box { flex-basis: 100%; } } /* Stile für OpenLayers Popups (Leaflet-Look-and-Feel) */ diff --git a/site_generator/templates/html_footer.template b/site_generator/templates/html_footer.template index 2bea999..09b6dc6 100755 --- a/site_generator/templates/html_footer.template +++ b/site_generator/templates/html_footer.template @@ -5,5 +5,41 @@ + + diff --git a/site_generator/templates/world_detail_page.template b/site_generator/templates/world_detail_page.template index 2e24b36..e6973a2 100755 --- a/site_generator/templates/world_detail_page.template +++ b/site_generator/templates/world_detail_page.template @@ -103,7 +103,8 @@ function fetchWorldStatus_%%current_world_key%%() { function fetchDataForElement(elementId, filePath, isRawText, prefixText, suffixText, isJsonPlayerList) { const el = document.getElementById(elementId); - if (!el && !isJsonPlayerList) return; + if (!el && !isJsonPlayerList) return; + fetch('/' + filePath + '?v=%%CACHE_BUSTER%%&t=' + new Date().getTime()) .then(r => { if (!r.ok) throw new Error('Datei ' + filePath + ' nicht erreichbar (' + r.status + ')'); return r.text(); }) .then(t => { @@ -111,9 +112,10 @@ function fetchDataForElement(elementId, filePath, isRawText, prefixText, suffixT if (t.trim() === "") { content = (elementId.startsWith("map-last-update") ? "unbekannt" : "Keine Daten verfügbar."); if (isJsonPlayerList && window.playerListLogic_%%current_world_key%%) { - window.playerListLogic_%%current_world_key%%.masterPlayerData = {}; - window.playerListLogic_%%current_world_key%%.updatePlayerMarkers({}); - window.playerListLogic_%%current_world_key%%.applyPlayerFiltersAndRender(); + const logic = window.playerListLogic_%%current_world_key%%; + logic.masterPlayerData = {}; + logic.updatePlayerMarkers({}); + logic.applyPlayerFiltersAndRender(); } } else { if (isJsonPlayerList && window.playerListLogic_%%current_world_key%%) { @@ -125,7 +127,11 @@ function fetchDataForElement(elementId, filePath, isRawText, prefixText, suffixT logic.applyPlayerFiltersAndRender(); } catch (e) { console.error("Fehler beim Parsen der Spielerdaten:", e); } return; - } else { content = isRawText ? t.replace(/\n/g, '
') : t; } + } else { + const dv = document.createElement('div'); + dv.textContent = t; + content = dv.innerHTML.replace(/\n/g, '
'); + } } if(el) { el.innerHTML = (prefixText || '') + content + (suffixText || ''); } }).catch(e => { @@ -168,6 +174,7 @@ document.addEventListener('DOMContentLoaded', function() { // Periodische Updates setInterval(fetchWorldStatus_%%current_world_key%%, 60000); setInterval(() => fetchDataForElement('map-last-update-text-%%current_world_key%%', '%%web_last_update_rel_path%%', true, '', ''), 60000); -setInterval(() => fetchDataForElement('player-info-%%current_world_key%%', '%%web_players_txt_rel_path%%', false, '', '', true), 70000); +// KORREKTUR: Intervall von 70000ms auf 30000ms (30 Sekunden) geändert +setInterval(() => fetchDataForElement('player-info-%%current_world_key%%', '%%web_players_txt_rel_path%%', false, '', '', true), 30000); setInterval(() => fetchDataForElement('weather-info-%%current_world_key%%', '%%web_weather_txt_rel_path%%', true, '

Wetter: ', '

'), 300000); diff --git a/site_generator/templates/world_detail_playerlist.template b/site_generator/templates/world_detail_playerlist.template index 44313f7..8668ac8 100644 --- a/site_generator/templates/world_detail_playerlist.template +++ b/site_generator/templates/world_detail_playerlist.template @@ -39,22 +39,6 @@ if (!window.playerListLogic_%%current_world_key%%) { source.clear(); // Entferne alle alten Marker - // KORREKTUR: Marker-Stile auf Kreise umgestellt - const adminIconStyle = new ol.style.Style({ - image: new ol.style.Circle({ - radius: 7, - fill: new ol.style.Fill({ color: 'rgba(255, 255, 0, 0.9)' }), // Gelb - stroke: new ol.style.Stroke({ color: '#fff', width: 2 }) - }) - }); - const playerIconStyle = new ol.style.Style({ - image: new ol.style.Circle({ - radius: 6, - fill: new ol.style.Fill({ color: 'rgba(255, 0, 0, 0.9)' }), // Rot - stroke: new ol.style.Stroke({ color: '#fff', width: 1 }) - }) - }); - const features = []; const now_epoch = Math.floor(Date.now() / 1000); const twentyFourHoursInSeconds = 24 * 60 * 60; @@ -72,13 +56,35 @@ if (!window.playerListLogic_%%current_world_key%%) { lastLoginFormatted = formatTimestampForDisplay(p.last_login); } + // HINZUGEFÜGT: Erstelle den kombinierten Stil aus Kreis und Text + const circleStyle = new ol.style.Style({ + image: new ol.style.Circle({ + radius: isAdmin ? 7 : 6, + fill: new ol.style.Fill({ color: isAdmin ? 'rgba(255, 190, 0, 0.9)' : 'rgba(255, 40, 40, 0.9)' }), + stroke: new ol.style.Stroke({ color: '#ffffff', width: isAdmin ? 2 : 1 }) + }) + }); + + const textStyle = new ol.style.Style({ + text: new ol.style.Text({ + text: p.name, + font: 'bold 11px "Helvetica Neue", Arial, sans-serif', + fill: new ol.style.Fill({ color: '#ffffff' }), + stroke: new ol.style.Stroke({ color: 'rgba(0, 0, 0, 0.9)', width: 3 }), + offsetY: -20, // Positioniert den Text über dem Kreismittelpunkt + textAlign: 'center' + }) + }); + const feature = new ol.Feature({ geometry: new ol.geom.Point(coords), playerData: p, statusDotClass: statusDotClass, lastLoginFormatted: lastLoginFormatted }); - feature.set('style', isAdmin ? adminIconStyle : playerIconStyle); + + // Setze den kombinierten Stil (Kreis und Text) für das Feature + feature.set('style', [circleStyle, textStyle]); features.push(feature); } source.addFeatures(features); diff --git a/site_generator/templates/world_detail_radar.template b/site_generator/templates/world_detail_radar.template index ee83e08..94bd7de 100644 --- a/site_generator/templates/world_detail_radar.template +++ b/site_generator/templates/world_detail_radar.template @@ -7,7 +7,7 @@
-
+