Integrate sync_tpads.sh to show Teleporter Pdas in a new created overlay
This commit is contained in:
parent
a3556c44e1
commit
e57dc8f498
4 changed files with 243 additions and 32 deletions
|
|
@ -172,6 +172,7 @@ generate_world_detail_page() {
|
||||||
"web_tiles_rel_path" "$web_tiles_rel_path" \
|
"web_tiles_rel_path" "$web_tiles_rel_path" \
|
||||||
"web_map_info_rel_path" "${WEB_MAPS_BASE_SUBDIR}/${current_world_key}/map_info.txt" \
|
"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_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" \
|
"map_background_color" "$map_background_color" \
|
||||||
"DEFAULT_PLAYER_SKIN_URL" "$DEFAULT_PLAYER_SKIN_URL"
|
"DEFAULT_PLAYER_SKIN_URL" "$DEFAULT_PLAYER_SKIN_URL"
|
||||||
local WORLD_RADAR_HTML; WORLD_RADAR_HTML=$(<"$temp_radar_file")
|
local WORLD_RADAR_HTML; WORLD_RADAR_HTML=$(<"$temp_radar_file")
|
||||||
|
|
|
||||||
|
|
@ -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 { 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:hover { background-color: #666; text-decoration: none; }
|
||||||
.scroll-to-top-btn.show { display: block; }
|
.scroll-to-top-btn.show { display: block; }
|
||||||
/* Stile für OpenLayers Popups (Leaflet-Look-and-Feel) */
|
/* Stile für OpenLayers Popups */
|
||||||
.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 { 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, .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; }
|
.ol-popup:after { border-top-color: #303030; border-width: 10px; left: 48px; margin-left: -10px; }
|
||||||
|
|
@ -236,36 +236,33 @@ a.read-more-link { font-size: 0.9em; font-weight: bold; }
|
||||||
.admin-box { flex-basis: 100%; }
|
.admin-box { flex-basis: 100%; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* KORREKTUR: Stile für OpenLayers Layer Switcher */
|
/* Stile für OpenLayers Layer Switcher (VOM BENUTZER KORRIGIERT) */
|
||||||
|
|
||||||
.layer-switcher.shown {
|
.layer-switcher.shown {
|
||||||
background-color: #303030 !important;
|
background-color: #303030 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layer-switcher button {
|
.layer-switcher button {
|
||||||
font-size: 1.2em !important;
|
font-size: 1.2em !important;
|
||||||
|
width: 40px !important;
|
||||||
|
height: 40px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layer-switcher button:hover, .layer-switcher button:focus {
|
.layer-switcher button:hover, .layer-switcher button:focus {
|
||||||
background-color: #666 !important;
|
background-color: #666 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layer-switcher .panel {
|
.layer-switcher .panel {
|
||||||
background-color: #303030 !important;
|
background-color: #303030 !important;
|
||||||
border: 1px solid #555 !important;
|
border: 1px solid #555 !important;
|
||||||
color: #ddd !important;
|
color: #ddd !important;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
padding: 5px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layer-switcher ul {
|
.layer-switcher ul {
|
||||||
padding-left: 1em;
|
padding-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layer-switcher label {
|
.layer-switcher label {
|
||||||
color: #ddd !important;
|
color: #ddd !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NEU: Styling für Checkboxen im Layer-Switcher */
|
/* Styling für Checkboxen im Layer-Switcher */
|
||||||
.layer-switcher input[type=checkbox],
|
.layer-switcher input[type=checkbox],
|
||||||
.layer-switcher input[type=radio] {
|
.layer-switcher input[type=radio] {
|
||||||
accent-color: #FFA500;
|
accent-color: #FFA500;
|
||||||
|
|
@ -273,7 +270,7 @@ a.read-more-link { font-size: 0.9em; font-weight: bold; }
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NEU: Vergrößerung der Standard-Karten-Buttons */
|
/* Vergrößerung der Standard-Karten-Buttons */
|
||||||
.ol-zoom .ol-zoom-in,
|
.ol-zoom .ol-zoom-in,
|
||||||
.ol-zoom .ol-zoom-out,
|
.ol-zoom .ol-zoom-out,
|
||||||
.ol-rotate .ol-rotate-reset,
|
.ol-rotate .ol-rotate-reset,
|
||||||
|
|
@ -284,3 +281,17 @@ a.read-more-link { font-size: 0.9em; font-weight: bold; }
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
padding: 0;
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ window.olMap_%%current_world_key%% = null;
|
||||||
window.playerMarkerSource_%%current_world_key%% = new ol.source.Vector();
|
window.playerMarkerSource_%%current_world_key%% = new ol.source.Vector();
|
||||||
window.parentAreaLayerSource_%%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.subAreaLayerSource_%%current_world_key%% = new ol.source.Vector();
|
||||||
|
window.tpadMarkerSource_%%current_world_key%% = new ol.source.Vector();
|
||||||
window.mapData_%%current_world_key%% = {};
|
window.mapData_%%current_world_key%% = {};
|
||||||
|
|
||||||
// Konvertiert Minetest-Koordinaten in OpenLayers-Pixel-Koordinaten
|
// Konvertiert Minetest-Koordinaten in OpenLayers-Pixel-Koordinaten
|
||||||
|
|
@ -41,6 +42,23 @@ function convertMinetestToOpenLayers_%%current_world_key%%(posX, posZ) {
|
||||||
return [pixelX, pixelY];
|
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) {
|
function createPolygonFromPos_%%current_world_key%%(pos1, pos2) {
|
||||||
const p1 = convertMinetestToOpenLayers_%%current_world_key%%(pos1.x, pos1.z);
|
const p1 = convertMinetestToOpenLayers_%%current_world_key%%(pos1.x, pos1.z);
|
||||||
const p2 = convertMinetestToOpenLayers_%%current_world_key%%(pos2.x, pos2.z);
|
const p2 = convertMinetestToOpenLayers_%%current_world_key%%(pos2.x, pos2.z);
|
||||||
|
|
@ -58,11 +76,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
const mapContainer = document.getElementById('ol-map-container-%%current_world_key%%');
|
const mapContainer = document.getElementById('ol-map-container-%%current_world_key%%');
|
||||||
const mapInfoPath = '/%%web_map_info_rel_path%%?v=' + new Date().getTime();
|
const mapInfoPath = '/%%web_map_info_rel_path%%?v=' + new Date().getTime();
|
||||||
const areasPath = '/%%web_areas_json_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([
|
Promise.all([
|
||||||
fetch(mapInfoPath).then(res => { if (!res.ok) throw new Error('map_info.txt'); return res.text(); }),
|
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(areasPath).then(res => { if (!res.ok) return {}; return res.json(); }),
|
||||||
]).then(([mapInfoText, areaData]) => {
|
fetch(tpadsPath).then(res => { if (!res.ok) return []; return res.json(); })
|
||||||
|
]).then(([mapInfoText, areaData, tpadData]) => {
|
||||||
|
|
||||||
const mapInfo = (text => {
|
const mapInfo = (text => {
|
||||||
const data = {};
|
const data = {};
|
||||||
|
|
@ -123,15 +143,22 @@ 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 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 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,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="turquoise" stroke="black" stroke-width="0.5" d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/></svg>',
|
||||||
|
scale: 1.5
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
const playerLayer = new ol.layer.Vector({ source: window.playerMarkerSource_%%current_world_key%%, style: f => f.get('style'), title: 'Spieler' });
|
const playerLayer = new ol.layer.Vector({ source: window.playerMarkerSource_%%current_world_key%%, style: f => f.get('style'), title: 'Spieler' });
|
||||||
const parentAreaLayer = new ol.layer.Vector({ source: window.parentAreaLayerSource_%%current_world_key%%, style: parentAreaStyle, title: 'Grundstücke' });
|
const tpadLayer = new ol.layer.Vector({ source: window.tpadMarkerSource_%%current_world_key%%, style: tpadStyle, title: 'Teleporter' });
|
||||||
// KORREKTUR: Die 'title'-Eigenschaft wurde von diesem Layer entfernt
|
const parentAreaLayer = new ol.layer.Vector({ source: window.parentAreaLayerSource_%%current_world_key%%, style: parentAreaStyle, title: 'Grundstücke', visible: false });
|
||||||
const subAreaLayer = new ol.layer.Vector({ source: window.subAreaLayerSource_%%current_world_key%%, style: subAreaStyle, visible: false });
|
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, playerLayer] });
|
const overlayLayers = new ol.layer.Group({ title: 'Overlays', layers: [subAreaLayer, parentAreaLayer, tpadLayer, playerLayer] });
|
||||||
|
|
||||||
const view = new ol.View({ projection: projection, center: ol.extent.getCenter(extent), resolutions: viewResolutions });
|
const view = new ol.View({ projection: projection, center: ol.extent.getCenter(extent), resolutions: viewResolutions, maxZoom: viewResolutions.length - 1 });
|
||||||
|
|
||||||
const popupContainer = document.getElementById('popup-%%current_world_key%%');
|
const popupContainer = document.getElementById('popup-%%current_world_key%%');
|
||||||
const popupContent = document.getElementById('popup-content-%%current_world_key%%');
|
const popupContent = document.getElementById('popup-content-%%current_world_key%%');
|
||||||
|
|
@ -139,7 +166,15 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
const overlay = new ol.Overlay({ element: popupContainer, autoPan: { animation: { duration: 250 } } });
|
const overlay = new ol.Overlay({ element: popupContainer, autoPan: { animation: { duration: 250 } } });
|
||||||
popupCloser.onclick = () => { overlay.setPosition(undefined); popupCloser.blur(); return false; };
|
popupCloser.onclick = () => { overlay.setPosition(undefined); popupCloser.blur(); return false; };
|
||||||
|
|
||||||
const mapControls = [ new ol.control.Zoom(), new ol.control.Rotate(), new ol.control.Attribution({ collapsible: false }), new ol.control.FullScreen(), new ol.control.LayerSwitcher() ];
|
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 ];
|
||||||
|
|
||||||
window.olMap_%%current_world_key%% = new ol.Map({
|
window.olMap_%%current_world_key%% = new ol.Map({
|
||||||
controls: mapControls,
|
controls: mapControls,
|
||||||
|
|
@ -160,6 +195,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
window.parentAreaLayerSource_%%current_world_key%%.addFeature(feature);
|
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) {
|
map.on('click', function(evt) {
|
||||||
overlay.setPosition(undefined);
|
overlay.setPosition(undefined);
|
||||||
|
|
@ -173,17 +216,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
if (playerData) {
|
if (playerData) {
|
||||||
const statusDotClass = playerFeature.get('statusDotClass');
|
const statusDotClass = playerFeature.get('statusDotClass');
|
||||||
const lastLoginFormatted = playerFeature.get('lastLoginFormatted');
|
const lastLoginFormatted = playerFeature.get('lastLoginFormatted');
|
||||||
|
// KORREKTUR: Layout des Spieler-Popups
|
||||||
const popupHTML = `
|
const popupHTML = `
|
||||||
<div class='popup-player-box'>
|
<div class='popup-player-box'>
|
||||||
<div class="player-header">
|
<div class="player-header"><div class="player-identity">
|
||||||
<div class="player-identity">
|
<img src="/images/players/${encodeURIComponent(playerData.name)}.png?v=%%CACHE_BUSTER%%" class="player-icon" onerror="this.onerror=null;this.src='/%%DEFAULT_PLAYER_SKIN_URL%%?v=%%CACHE_BUSTER%%';">
|
||||||
<img src="/images/players/${encodeURIComponent(playerData.name)}.png?v=%%CACHE_BUSTER%%" class="player-icon" onerror="this.onerror=null;this.src='/%%DEFAULT_PLAYER_SKIN_URL%%?v=%%CACHE_BUSTER%%';">
|
<span class="player-name-status">
|
||||||
<span class="player-name-status">
|
<span class="status-dot ${statusDotClass}" title="Letzter Login: ${lastLoginFormatted}"></span>
|
||||||
<span class="status-dot ${statusDotClass}" title="Letzter Login: ${lastLoginFormatted}"></span>
|
<strong class="player-name">${playerData.name}</strong>
|
||||||
<strong class="player-name">${playerData.name}</strong>
|
</span>
|
||||||
</span>
|
</div></div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr class="privilege-separator">
|
<hr class="privilege-separator">
|
||||||
<div class="popup-player-vitals">
|
<div class="popup-player-vitals">
|
||||||
<span class="vital"><span class="icon">❤️</span> ${playerData.hp ?? '?'}</span>
|
<span class="vital"><span class="icon">❤️</span> ${playerData.hp ?? '?'}</span>
|
||||||
|
|
@ -197,6 +239,31 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tpadFeature = map.forEachFeatureAtPixel(evt.pixel, (f, l) => l === tpadLayer ? f : undefined);
|
||||||
|
if (tpadFeature) {
|
||||||
|
window.subAreaLayerSource_%%current_world_key%%.clear();
|
||||||
|
subAreaLayer.setVisible(false);
|
||||||
|
const tpadData = tpadFeature.get('tpadData');
|
||||||
|
if (tpadData) {
|
||||||
|
// KORREKTUR: Layout des TPAD-Popups
|
||||||
|
const popupHTML = `
|
||||||
|
<div class='popup-player-box'>
|
||||||
|
<div class="player-header"><div class="player-identity">
|
||||||
|
<img src="/images/tpad.png?v=%%CACHE_BUSTER%%" class="player-icon" alt="Teleporter">
|
||||||
|
<div class="player-name-status" style="display: block;">
|
||||||
|
<strong class="player-name">${tpadData.name}</strong><br>
|
||||||
|
<span style="font-size: 0.9em; color: #ccc;">Teleporter</span>
|
||||||
|
</div>
|
||||||
|
</div></div>
|
||||||
|
<hr class="privilege-separator">
|
||||||
|
<div>Besitzer: ${tpadData.owner}</div>
|
||||||
|
</div>`;
|
||||||
|
popupContent.innerHTML = popupHTML;
|
||||||
|
overlay.setPosition(tpadFeature.getGeometry().getCoordinates());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
window.subAreaLayerSource_%%current_world_key%%.clear();
|
window.subAreaLayerSource_%%current_world_key%%.clear();
|
||||||
const areaFeature = map.forEachFeatureAtPixel(evt.pixel, (f, l) => l === parentAreaLayer ? f : undefined);
|
const areaFeature = map.forEachFeatureAtPixel(evt.pixel, (f, l) => l === parentAreaLayer ? f : undefined);
|
||||||
if (areaFeature) {
|
if (areaFeature) {
|
||||||
|
|
@ -212,12 +279,27 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
} else {
|
} else {
|
||||||
subAreaLayer.setVisible(false);
|
subAreaLayer.setVisible(false);
|
||||||
}
|
}
|
||||||
let popupHTML = `<strong>${areaData.name}</strong><br>Besitzer: ${areaData.owner}`;
|
|
||||||
|
let parcelsHTML = '';
|
||||||
if (areaData.sub_areas && areaData.sub_areas.length > 0) {
|
if (areaData.sub_areas && areaData.sub_areas.length > 0) {
|
||||||
popupHTML += `<hr class="privilege-separator"><p><strong>Parzellen:</strong></p><ul>`;
|
parcelsHTML += `<div style="margin-top: 5px;"><p style="margin: 0; font-weight: bold;">Parzellen:</p><ul>`;
|
||||||
areaData.sub_areas.forEach(sub => { popupHTML += `<li>${sub.name} (${sub.owner})</li>`; });
|
areaData.sub_areas.forEach(sub => { parcelsHTML += `<li style="font-size: 0.9em;">${sub.name} (${sub.owner})</li>`; });
|
||||||
popupHTML += `</ul>`;
|
parcelsHTML += `</ul></div>`;
|
||||||
}
|
}
|
||||||
|
// KORREKTUR: Layout des Grundstück-Popups
|
||||||
|
const popupHTML = `
|
||||||
|
<div class='popup-player-box'>
|
||||||
|
<div class="player-header"><div class="player-identity">
|
||||||
|
<img src="/images/area.png?v=%%CACHE_BUSTER%%" class="player-icon" alt="Grundstück">
|
||||||
|
<div class="player-name-status" style="display: block;">
|
||||||
|
<strong class="player-name">${areaData.name}</strong><br>
|
||||||
|
<span style="font-size: 0.9em; color: #ccc;">Grundstück</span>
|
||||||
|
</div>
|
||||||
|
</div></div>
|
||||||
|
<hr class="privilege-separator">
|
||||||
|
<div>Besitzer: ${areaData.owner}</div>
|
||||||
|
${parcelsHTML}
|
||||||
|
</div>`;
|
||||||
popupContent.innerHTML = popupHTML;
|
popupContent.innerHTML = popupHTML;
|
||||||
overlay.setPosition(evt.coordinate);
|
overlay.setPosition(evt.coordinate);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -231,7 +313,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error("Fehler beim Initialisieren der Karte oder der Grundstücksdaten:", error);
|
console.error("Fehler beim Initialisieren der Kartendaten:", error);
|
||||||
mapContainer.innerHTML = "<p><em>Karte konnte nicht initialisiert werden: " + error.message + "</em></p>";
|
mapContainer.innerHTML = "<p><em>Karte konnte nicht initialisiert werden: " + error.message + "</em></p>";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
117
sync_tpads.sh
Executable file
117
sync_tpads.sh
Executable file
|
|
@ -0,0 +1,117 @@
|
||||||
|
#!/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("\\[\"\\((?<posX>[^,]*),(?<posY>[^,]*),(?<posZ>[^)]*)\\)\"\\]=\\{(?<props>[^\\}]*)\\}"; "g")]) as $entries |
|
||||||
|
$entries | map(
|
||||||
|
(.props | split(",") | map(split("=")) | map({(.[0]|trim): .[1]|trim}) | add) as $prop_obj |
|
||||||
|
select($prop_obj.type == "4") |
|
||||||
|
{
|
||||||
|
owner: $owner,
|
||||||
|
name: ($prop_obj.name | gsub("\""; "")),
|
||||||
|
posX: (.posX | tonumber),
|
||||||
|
posY: (.posY | tonumber),
|
||||||
|
posZ: (.posZ | tonumber)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) |
|
||||||
|
flatten
|
||||||
|
' > "$TPADS_JSON_TMP_FILE_PATH"
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
log_message "${WORLD_KEY}" "FEHLER: jq-Transformation fehlgeschlagen."
|
||||||
|
rm -f "$TPADS_JSON_TMP_FILE_PATH"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Temporäre Datei an den Zielort verschieben
|
||||||
|
mkdir -p "$TPADS_JSON_TARGET_DIR"
|
||||||
|
mv "$TPADS_JSON_TMP_FILE_PATH" "$TPADS_JSON_FILE_PATH"
|
||||||
|
|
||||||
|
log_message "${WORLD_KEY}" "tpads.txt erfolgreich synchronisiert nach ${TPADS_JSON_FILE_PATH}"
|
||||||
|
|
||||||
|
exit 0
|
||||||
Loading…
Add table
Add a link
Reference in a new issue