1
0
forked from MTSR/mapserver

Add support for realtime APercy Airutils-based planes

This commit is contained in:
Kenneth Watson 2022-08-13 11:52:15 +08:00 committed by Buckaroo Banzai
parent 2c4ee1d130
commit 84253939c4
13 changed files with 198 additions and 8 deletions

View File

@ -89,6 +89,7 @@ func ParseConfig(filename string) (*Config, error) {
Minecart: false, Minecart: false,
Locator: false, Locator: false,
Signs: true, Signs: true,
MapserverAirutils: true,
} }
mapblockaccessor := MapBlockAccessorConfig{ mapblockaccessor := MapBlockAccessorConfig{

View File

@ -61,6 +61,7 @@ type MapObjectConfig struct {
Minecart bool `json:"minecart"` Minecart bool `json:"minecart"`
Locator bool `json:"locator"` Locator bool `json:"locator"`
Signs bool `json:"signs"` Signs bool `json:"signs"`
MapserverAirutils bool `json:"mapserver_airutils"`
} }
type WebApiConfig struct { type WebApiConfig struct {

View File

@ -59,7 +59,8 @@
"trainsignal": true, "trainsignal": true,
"minecart": false, "minecart": false,
"locator": false, "locator": false,
"signs": true "signs": true,
"mapserver_airutils": true
}, },
"mapblockaccessor": { "mapblockaccessor": {
"expiretime": "15s", "expiretime": "15s",

View File

@ -8,6 +8,7 @@ you get more realtime-data from within your minetest-world:
* Current players with their positions * Current players with their positions
* Current time and max lag * Current time and max lag
* Planes using the [Airutils library](https://github.com/APercy/airutils) by APercy, if installed
You can use the `mapserver-mod` either passive or active: You can use the `mapserver-mod` either passive or active:
* *Passive* Makes some additional markers available (POI, Labels, etc) * *Passive* Makes some additional markers available (POI, Labels, etc)

View File

@ -22,6 +22,7 @@ import BorderOverlay from './overlays/BorderOverlay.js';
import TrainOverlay from './overlays/TrainOverlay.js'; import TrainOverlay from './overlays/TrainOverlay.js';
import TrainsignalOverlay from './overlays/TrainsignalOverlay.js'; import TrainsignalOverlay from './overlays/TrainsignalOverlay.js';
import SignOverlay from './overlays/SignOverlay.js'; import SignOverlay from './overlays/SignOverlay.js';
import AirUtilsPlanesOverlay from "./overlays/AirUtilsPlanesOverlay.js";
export default function(cfg, map, overlays, wsChannel){ export default function(cfg, map, overlays, wsChannel){
@ -199,4 +200,11 @@ export default function(cfg, map, overlays, wsChannel){
} }
} }
if (cfg.mapobjects.mapserver_airutils) {
overlays.Planes = new AirUtilsPlanesOverlay();
if (isDefault("mapserver_airutils")) {
map.addLayer(overlays.Planes);
}
}
} }

View File

@ -0,0 +1,165 @@
import wsChannel from '../../WebSocketChannel.js';
import layerMgr from '../../LayerManager.js';
let planes = [];
let icons = {
"default": {
url: "pics/airutils_planes/supercub.png",
size: 48
},
"hidroplane:hidro": {
url: "pics/airutils_planes/hidro.png",
size: 48
},
"supercub:supercub": {
url: "pics/airutils_planes/supercub.png",
size: 48
},
"pa28:pa28": {
url: "pics/airutils_planes/pa28.png",
size: 64
},
"trike:trike": {
url: "pics/airutils_planes/trike.png",
size: 40
},
"ju52:ju52": {
url: "pics/airutils_planes/ju52.png",
size: 72
},
"steampunk_blimp:blimp": {
url: "pics/airutils_planes/blimp.png",
size: 96
},
};
// listening for realtime updates
wsChannel.addListener("minetest-info", function(info) {
planes = info.airutils_planes || [];
});
export default L.LayerGroup.extend({
initialize: function() {
L.LayerGroup.prototype.initialize.call(this);
this.currentObjects = {}; // id => marker
this.reDraw = this.reDraw.bind(this);
this.onMinetestUpdate = this.onMinetestUpdate.bind(this);
},
createPopup: function(plane) {
let name = plane.name;
if (!name) name = plane.entity.substring(plane.entity.indexOf(":")+1);
let html = "<b>" + name + "</b><br>";
html += "<hr>";
html += "<b>Owner:</b> " + plane.owner + "<br>";
html += "<b>Pilot:</b> " + (plane.driver ? plane.driver : "-") + "<br>";
html += "<b>Passengers:</b> " + (plane.passenger ? plane.passenger : "-") + "<br>";
return html;
},
createMarker: function(plane) {
let marker = L.marker([plane.pos.z, plane.pos.x], {icon: this.getIcon(plane)});
marker.bindPopup(this.createPopup(plane));
return marker;
},
getIcon: function(plane) {
let icon = icons[plane.entity];
if (!icon) icon = icons.default;
return L.divIcon({
html: `<div style="display:inline-block;width:${icon.size}px;height:${icon.size}px;transform:rotate(${plane.yaw*-1}rad);mask:url(${icon.url}) center/contain;-webkit-mask:url(${icon.url}) center/contain;background:${plane.color}">
<img src="${icon.url}" style="width:${icon.size}px;height:${icon.size}px;filter:saturate(0%);mix-blend-mode:multiply;" alt="${plane.name}">
</div>`,
className: '', // don't use leaflet default of a white block
iconSize: [icon.size, icon.size],
iconAnchor: [icon.size/2, icon.size/2],
popupAnchor: [0, -(icon.size/2)]
});
},
isInCurrentLayer: function(plane) {
let mapLayer = layerMgr.getCurrentLayer();
return (
plane.pos.y >= (mapLayer.from*16) &&
plane.pos.y <= ((mapLayer.to*16) + 15)
);
},
onMinetestUpdate: function(/*info*/) {
planes.forEach(plane => {
let isInLayer = this.isInCurrentLayer(plane);
if (!isInLayer) {
if (this.currentObjects[plane.id]) {
//player is displayed and not on the layer anymore
//Remove the marker and reference
this.currentObjects[plane.id].remove();
delete this.currentObjects[plane.id];
}
return;
}
if (this.currentObjects[plane.id]) {
//marker exists
let marker = this.currentObjects[plane.id];
marker.setLatLng([plane.pos.z, plane.pos.x]);
marker.setIcon(this.getIcon(plane));
marker.setPopupContent(this.createPopup(plane));
} else {
//marker does not exist
let marker = this.createMarker(plane);
marker.addTo(this);
this.currentObjects[plane.id] = marker;
}
});
Object.keys(this.currentObjects).forEach(existingId => {
let planeIsActive = planes.find(function(p) {
return p.id == existingId;
});
if (!planeIsActive) {
//player
this.currentObjects[existingId].remove();
delete this.currentObjects[existingId];
}
});
},
reDraw: function() {
this.currentObjects = {};
this.clearLayers();
planes.forEach(plane => {
if (!this.isInCurrentLayer(plane)) {
//not in current layer
return;
}
let marker = this.createMarker(plane);
marker.addTo(this);
this.currentObjects[plane.id] = marker;
});
},
onAdd: function(/*map*/) {
wsChannel.addListener("minetest-info", this.onMinetestUpdate);
this.reDraw();
},
onRemove: function(/*map*/) {
this.clearLayers();
wsChannel.removeListener("minetest-info", this.onMinetestUpdate);
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -53,14 +53,27 @@ type Player struct {
//TODO: stamina, skin, etc //TODO: stamina, skin, etc
} }
type AirUtilsPlane struct {
Id string `json:"id"`
Entity string `json:"entity"`
Name string `json:"name"`
Pos GenericPos `json:"pos"`
Owner string `json:"owner"`
Driver string `json:"driver"`
Passenger string `json:"passenger"`
Color string `json:"color"`
Yaw float64 `json:"yaw"`
}
type MinetestInfo struct { type MinetestInfo struct {
MaxLag float64 `json:"max_lag"` MaxLag float64 `json:"max_lag"`
Players []*Player `json:"players"` Players []*Player `json:"players"`
Trains []*Train `json:"trains"` Trains []*Train `json:"trains"`
Signals []*Signal `json:"signals"` Signals []*Signal `json:"signals"`
Minecarts []*Minecart `json:"minecarts"` Minecarts []*Minecart `json:"minecarts"`
Time float64 `json:"time"` AirUtilsPlanes []*AirUtilsPlane `json:"airutils_planes"`
Uptime float64 `json:"uptime"` Time float64 `json:"time"`
Uptime float64 `json:"uptime"`
} }
var LastStats *MinetestInfo var LastStats *MinetestInfo