Implement update handling for areas/lines/circles
This commit is contained in:
parent
a809b68bb2
commit
e77a777a66
256
src/api.ts
256
src/api.ts
@ -1,11 +1,18 @@
|
|||||||
import axios, {AxiosResponse} from 'axios';
|
import axios, {AxiosResponse} from 'axios';
|
||||||
import {
|
import {
|
||||||
DynmapArea, DynmapCircle,
|
DynmapArea,
|
||||||
|
DynmapCircle,
|
||||||
DynmapComponentConfig,
|
DynmapComponentConfig,
|
||||||
DynmapConfigurationResponse, DynmapLine, DynmapMap, DynmapMarker, DynmapMarkerSet, DynmapMessageConfig,
|
DynmapConfigurationResponse,
|
||||||
|
DynmapLine,
|
||||||
|
DynmapMap,
|
||||||
|
DynmapMarker,
|
||||||
|
DynmapMarkerSet,
|
||||||
|
DynmapMarkerSetUpdates,
|
||||||
|
DynmapMessageConfig,
|
||||||
DynmapPlayer,
|
DynmapPlayer,
|
||||||
DynmapServerConfig,
|
DynmapServerConfig, DynmapUpdate,
|
||||||
DynmapUpdateResponse,
|
DynmapUpdateResponse, DynmapUpdates,
|
||||||
DynmapWorld
|
DynmapWorld
|
||||||
} from "@/dynmap";
|
} from "@/dynmap";
|
||||||
|
|
||||||
@ -166,27 +173,29 @@ function buildMarkers(data: any): Map<string, DynmapMarker> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const marker = data[key];
|
markers.set(key, buildMarker(data[key]));
|
||||||
|
|
||||||
markers.set(key, {
|
|
||||||
label: marker.label || '',
|
|
||||||
location: {
|
|
||||||
x: marker.x || 0,
|
|
||||||
y: marker.y || 0,
|
|
||||||
z: marker.z || 0,
|
|
||||||
},
|
|
||||||
dimensions: marker.dim ? marker.dim.split('x') : [16, 16],
|
|
||||||
icon: marker.icon || "default",
|
|
||||||
isHTML: marker.markup || false,
|
|
||||||
minZoom: marker.minzoom || undefined,
|
|
||||||
maxZoom: marker.maxZoom || undefined,
|
|
||||||
popupContent: marker.desc || undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return markers;
|
return markers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildMarker(marker: any): DynmapMarker {
|
||||||
|
return {
|
||||||
|
label: marker.label || '',
|
||||||
|
location: {
|
||||||
|
x: marker.x || 0,
|
||||||
|
y: marker.y || 0,
|
||||||
|
z: marker.z || 0,
|
||||||
|
},
|
||||||
|
dimensions: marker.dim ? marker.dim.split('x') : [16, 16],
|
||||||
|
icon: marker.icon || "default",
|
||||||
|
isHTML: marker.markup || false,
|
||||||
|
minZoom: marker.minzoom || undefined,
|
||||||
|
maxZoom: marker.maxZoom || undefined,
|
||||||
|
popupContent: marker.desc || undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function buildAreas(data: any): Map<string, DynmapArea> {
|
function buildAreas(data: any): Map<string, DynmapArea> {
|
||||||
const areas = Object.freeze(new Map()) as Map<string, DynmapArea>;
|
const areas = Object.freeze(new Map()) as Map<string, DynmapArea>;
|
||||||
|
|
||||||
@ -195,30 +204,32 @@ function buildAreas(data: any): Map<string, DynmapArea> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const area = data[key];
|
areas.set(key, buildArea(data[key]));
|
||||||
|
|
||||||
areas.set(key, {
|
|
||||||
style: {
|
|
||||||
color: area.color || '#ff0000',
|
|
||||||
opacity: area.opacity || 1,
|
|
||||||
weight: area.weight || 1,
|
|
||||||
fillColor: area.fillcolor || '#ff0000',
|
|
||||||
fillOpacity: area.fillopacity || 0,
|
|
||||||
},
|
|
||||||
label: area.label || '',
|
|
||||||
isHTML: area.markup || false,
|
|
||||||
x: area.x || [0, 0],
|
|
||||||
y: [area.ybottom || 0, area.ytop || 0],
|
|
||||||
z: area.z || [0, 0],
|
|
||||||
minZoom: area.minzoom || undefined,
|
|
||||||
maxZoom: area.maxZoom || undefined,
|
|
||||||
popupContent: area.desc || undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return areas;
|
return areas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildArea(area: any): DynmapArea {
|
||||||
|
return {
|
||||||
|
style: {
|
||||||
|
color: area.color || '#ff0000',
|
||||||
|
opacity: area.opacity || 1,
|
||||||
|
weight: area.weight || 1,
|
||||||
|
fillColor: area.fillcolor || '#ff0000',
|
||||||
|
fillOpacity: area.fillopacity || 0,
|
||||||
|
},
|
||||||
|
label: area.label || '',
|
||||||
|
isHTML: area.markup || false,
|
||||||
|
x: area.x || [0, 0],
|
||||||
|
y: [area.ybottom || 0, area.ytop || 0],
|
||||||
|
z: area.z || [0, 0],
|
||||||
|
minZoom: area.minzoom || undefined,
|
||||||
|
maxZoom: area.maxZoom || undefined,
|
||||||
|
popupContent: area.desc || undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function buildLines(data: any): Map<string, DynmapLine> {
|
function buildLines(data: any): Map<string, DynmapLine> {
|
||||||
const lines = Object.freeze(new Map()) as Map<string, DynmapLine>;
|
const lines = Object.freeze(new Map()) as Map<string, DynmapLine>;
|
||||||
|
|
||||||
@ -227,28 +238,30 @@ function buildLines(data: any): Map<string, DynmapLine> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const line = data[key];
|
lines.set(key, buildLine(data[key]));
|
||||||
|
|
||||||
lines.set(key, {
|
|
||||||
x: line.x || [0, 0],
|
|
||||||
y: line.y || [0, 0],
|
|
||||||
z: line.z || [0, 0],
|
|
||||||
style: {
|
|
||||||
color: line.color || '#ff0000',
|
|
||||||
opacity: line.opacity || 1,
|
|
||||||
weight: line.weight || 1,
|
|
||||||
},
|
|
||||||
label: line.label || '',
|
|
||||||
isHTML: line.markup || false,
|
|
||||||
minZoom: line.minzoom || undefined,
|
|
||||||
maxZoom: line.maxZoom || undefined,
|
|
||||||
popupContent: line.desc || undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildLine(line: any): DynmapLine {
|
||||||
|
return {
|
||||||
|
x: line.x || [0, 0],
|
||||||
|
y: line.y || [0, 0],
|
||||||
|
z: line.z || [0, 0],
|
||||||
|
style: {
|
||||||
|
color: line.color || '#ff0000',
|
||||||
|
opacity: line.opacity || 1,
|
||||||
|
weight: line.weight || 1,
|
||||||
|
},
|
||||||
|
label: line.label || '',
|
||||||
|
isHTML: line.markup || false,
|
||||||
|
minZoom: line.minzoom || undefined,
|
||||||
|
maxZoom: line.maxZoom || undefined,
|
||||||
|
popupContent: line.desc || undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function buildCircles(data: any): Map<string, DynmapCircle> {
|
function buildCircles(data: any): Map<string, DynmapCircle> {
|
||||||
const circles = Object.freeze(new Map()) as Map<string, DynmapCircle>;
|
const circles = Object.freeze(new Map()) as Map<string, DynmapCircle>;
|
||||||
|
|
||||||
@ -257,34 +270,117 @@ function buildCircles(data: any): Map<string, DynmapCircle> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const circle = data[key];
|
circles.set(key, buildCircle(data[key]));
|
||||||
|
|
||||||
circles.set(key, {
|
|
||||||
location: {
|
|
||||||
x: circle.x || 0,
|
|
||||||
y: circle.y || 0,
|
|
||||||
z: circle.z || 0,
|
|
||||||
},
|
|
||||||
radius: [circle.xr || 0, circle.zr || 0],
|
|
||||||
style: {
|
|
||||||
fillColor: circle.fillcolor || '#ff0000',
|
|
||||||
fillOpacity: circle.fillopacity || 0,
|
|
||||||
color: circle.color || '#ff0000',
|
|
||||||
opacity: circle.opacity || 1,
|
|
||||||
weight: circle.weight || 1,
|
|
||||||
},
|
|
||||||
label: circle.label || '',
|
|
||||||
isHTML: circle.markup || false,
|
|
||||||
|
|
||||||
minZoom: circle.minzoom || undefined,
|
|
||||||
maxZoom: circle.maxZoom || undefined,
|
|
||||||
popupContent: circle.desc || undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return circles;
|
return circles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildCircle(circle: any): DynmapCircle {
|
||||||
|
return {
|
||||||
|
location: {
|
||||||
|
x: circle.x || 0,
|
||||||
|
y: circle.y || 0,
|
||||||
|
z: circle.z || 0,
|
||||||
|
},
|
||||||
|
radius: [circle.xr || 0, circle.zr || 0],
|
||||||
|
style: {
|
||||||
|
fillColor: circle.fillcolor || '#ff0000',
|
||||||
|
fillOpacity: circle.fillopacity || 0,
|
||||||
|
color: circle.color || '#ff0000',
|
||||||
|
opacity: circle.opacity || 1,
|
||||||
|
weight: circle.weight || 1,
|
||||||
|
},
|
||||||
|
label: circle.label || '',
|
||||||
|
isHTML: circle.markup || false,
|
||||||
|
|
||||||
|
minZoom: circle.minzoom || undefined,
|
||||||
|
maxZoom: circle.maxZoom || undefined,
|
||||||
|
popupContent: circle.desc || undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildUpdates(data: Array<any>): DynmapUpdates {
|
||||||
|
const updates = {
|
||||||
|
markerSets: new Map<string, DynmapMarkerSetUpdates>(),
|
||||||
|
tiles: new Map(),
|
||||||
|
chat: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const entry of data) {
|
||||||
|
switch(entry.type) {
|
||||||
|
case 'component': {
|
||||||
|
if(!entry.id) {
|
||||||
|
console.warn(`Ignoring component update without an ID`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!entry.set) {
|
||||||
|
console.warn(`Ignoring component update without a marker set`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(entry.ctype !== 'markers') {
|
||||||
|
console.warn(`Ignoring component with unknown ctype ${entry.ctype}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!updates.markerSets.has(entry.set)) {
|
||||||
|
updates.markerSets.set(entry.set, {
|
||||||
|
areaUpdates: [],
|
||||||
|
markerUpdates: [],
|
||||||
|
lineUpdates: [],
|
||||||
|
circleUpdates: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const markerSetUpdates = updates.markerSets.get(entry.set),
|
||||||
|
update: DynmapUpdate = {
|
||||||
|
id: entry.id,
|
||||||
|
removed: entry.msg.endsWith('deleted'),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if(entry.msg.startsWith("marker")) {
|
||||||
|
update.payload = update.removed ? undefined : buildMarker(entry);
|
||||||
|
markerSetUpdates!.markerUpdates.push(Object.freeze(update));
|
||||||
|
} else if(entry.msg.startsWith("area")) {
|
||||||
|
update.payload = update.removed ? undefined : buildArea(entry);
|
||||||
|
markerSetUpdates!.areaUpdates.push(Object.freeze(update));
|
||||||
|
|
||||||
|
} else if(entry.msg.startsWith("circle")) {
|
||||||
|
update.payload = update.removed ? undefined : buildCircle(entry);
|
||||||
|
markerSetUpdates!.circleUpdates.push(Object.freeze(update));
|
||||||
|
|
||||||
|
} else if(entry.msg.startsWith("line")) {
|
||||||
|
update.payload = update.removed ? undefined : buildLine(entry);
|
||||||
|
markerSetUpdates!.lineUpdates.push(Object.freeze(update));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'chat':
|
||||||
|
//TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'tile':
|
||||||
|
if(!entry.name || !entry.timestamp) {
|
||||||
|
console.warn(`Ignoring tile update without a name or timestamp`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
updates.tiles.set(entry.name, entry.timestamp);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.warn(`Ignoring unknown update type ${entry.type}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updates;
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getConfiguration(): Promise<DynmapConfigurationResponse> {
|
getConfiguration(): Promise<DynmapConfigurationResponse> {
|
||||||
return axios.get(window.config.url.configuration).then((response): DynmapConfigurationResponse => {
|
return axios.get(window.config.url.configuration).then((response): DynmapConfigurationResponse => {
|
||||||
@ -349,6 +445,7 @@ export default {
|
|||||||
configHash: data.configHash || 0,
|
configHash: data.configHash || 0,
|
||||||
timestamp: data.timestamp || 0,
|
timestamp: data.timestamp || 0,
|
||||||
players,
|
players,
|
||||||
|
updates: buildUpdates(data.updates || []),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -374,6 +471,7 @@ export default {
|
|||||||
lines = buildLines(set.lines || {});
|
lines = buildLines(set.lines || {});
|
||||||
|
|
||||||
sets.set(key, {
|
sets.set(key, {
|
||||||
|
id: key,
|
||||||
label: set.label || "Unnamed set",
|
label: set.label || "Unnamed set",
|
||||||
hidden: set.hidden || false,
|
hidden: set.hidden || false,
|
||||||
priority: set.layerprio || 0,
|
priority: set.layerprio || 0,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<GenericMarker v-for="[id, marker] in markerSet.markers" :options="marker" :key="id" :layer-group="layerGroup"></GenericMarker>
|
<GenericMarker v-for="[id, marker] in markerSet.markers" :options="marker" :key="id" :layer-group="layerGroup"></GenericMarker>
|
||||||
<Areas :areas="markerSet.areas" :layer-group="layerGroup"></Areas>
|
<Areas :layer-group="layerGroup" :set="markerSet"></Areas>
|
||||||
<Circles :circles="markerSet.circles" :layer-group="layerGroup"></Circles>
|
<Circles :layer-group="layerGroup" :set="markerSet"></Circles>
|
||||||
<Lines :lines="markerSet.lines" :layer-group="layerGroup"></Lines>
|
<Lines :layer-group="layerGroup" :set="markerSet"></Lines>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -1,237 +1,106 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, computed} from "@vue/runtime-core";
|
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
|
||||||
import L, {LatLngExpression} from 'leaflet';
|
import {LayerGroup, Polyline, Polygon} from 'leaflet';
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import {DynmapArea} from "@/dynmap";
|
import {DynmapArea, DynmapMarkerSet} from "@/dynmap";
|
||||||
|
import {ActionTypes} from "@/store/action-types";
|
||||||
|
import {createArea, updateArea} from "@/util/areas";
|
||||||
|
import Util from '@/util';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
areas: {
|
set: {
|
||||||
type: Object as () => Map<string, DynmapArea>,
|
type: Object as () => DynmapMarkerSet,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
layerGroup: {
|
layerGroup: {
|
||||||
type: Object as () => L.LayerGroup,
|
type: Object as () => LayerGroup,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setup() {
|
setup(props) {
|
||||||
|
let updateFrame = 0;
|
||||||
|
|
||||||
const store = useStore(),
|
const store = useStore(),
|
||||||
currentProjection = computed(() => store.state.currentProjection),
|
currentProjection = computed(() => store.state.currentProjection),
|
||||||
layers = Object.freeze(new Map()) as Map<string, L.Path>;
|
pendingUpdates = computed(() => {
|
||||||
|
const markerSetUpdates = store.state.pendingSetUpdates.get(props.set.id);
|
||||||
|
|
||||||
return {
|
return markerSetUpdates && markerSetUpdates.areaUpdates.length;
|
||||||
layers,
|
}),
|
||||||
currentProjection,
|
layers = Object.freeze(new Map()) as Map<string, Polygon | Polyline>,
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
createAreas = () => {
|
||||||
//FIXME: Prevent unnecessary repositioning when changing worlds
|
const converter = Util.getPointConverter();
|
||||||
currentProjection() {
|
|
||||||
const projection = useStore().state.currentProjection,
|
props.set.areas.forEach((area: DynmapArea, id: string) => {
|
||||||
latLng = (x: number, y: number, z: number) => {
|
const layer = createArea(area, converter);
|
||||||
return projection.locationToLatLng({x, y, z});
|
|
||||||
|
layers.set(id, layer);
|
||||||
|
props.layerGroup.addLayer(layer);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteArea = (id: string) => {
|
||||||
|
let area = layers.get(id) as Polyline;
|
||||||
|
|
||||||
|
if(!area) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
area.remove();
|
||||||
for (const [id, area] of this.areas) {
|
layers.delete(id);
|
||||||
this.updateArea(id, area, latLng);
|
},
|
||||||
|
|
||||||
|
handlePendingUpdates = () => {
|
||||||
|
useStore().dispatch(ActionTypes.POP_AREA_UPDATES, {
|
||||||
|
markerSet: props.set.id,
|
||||||
|
amount: 10,
|
||||||
|
}).then(updates => {
|
||||||
|
const converter = Util.getPointConverter();
|
||||||
|
|
||||||
|
for(const update of updates) {
|
||||||
|
if(update.removed) {
|
||||||
|
console.log(`Deleting area ${update.id}`);
|
||||||
|
deleteArea(update.id);
|
||||||
|
} else {
|
||||||
|
console.log(`Updating/creating area ${update.id}`);
|
||||||
|
layers.set(update.id, updateArea(layers.get(update.id), update.payload as DynmapArea, converter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pendingUpdates.value) {
|
||||||
|
console.log('More updates left, scheduling frame');
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
|
||||||
|
} else {
|
||||||
|
updateFrame = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//FIXME: Prevent unnecessary repositioning when changing worlds
|
||||||
|
watch(currentProjection, () => {
|
||||||
|
const converter = Util.getPointConverter();
|
||||||
|
|
||||||
|
for (const [id, area] of props.set.areas) {
|
||||||
|
updateArea(layers.get(id), area, converter);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
watch(pendingUpdates, (newValue, oldValue) => {
|
||||||
this.createAreas();
|
if(newValue && newValue > 0 && oldValue === 0 && !updateFrame) {
|
||||||
},
|
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
|
||||||
|
}
|
||||||
unmounted() {
|
});
|
||||||
|
|
||||||
|
onMounted(() => createAreas());
|
||||||
|
onUnmounted(() => updateFrame && cancelAnimationFrame(updateFrame));
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return null;
|
return null;
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
createAreas() {
|
|
||||||
const projection = useStore().state.currentProjection,
|
|
||||||
latLng = (x: number, y: number, z: number) => {
|
|
||||||
return projection.locationToLatLng({x, y, z});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.areas.forEach((area: DynmapArea, id: string) => {
|
|
||||||
this.createArea(id, area, latLng);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
createArea(id: string, options: DynmapArea, latLng: Function) {
|
|
||||||
const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0),
|
|
||||||
points = this.getPoints(options, latLng, outline),
|
|
||||||
area: L.Path = outline ? new L.Polyline(points, options.style) : new L.Polygon(points, options.style);
|
|
||||||
|
|
||||||
if (options.label) {
|
|
||||||
area.bindPopup(() => {
|
|
||||||
const popup = document.createElement('span');
|
|
||||||
|
|
||||||
if (options.popupContent) {
|
|
||||||
popup.classList.add('AreaPopup');
|
|
||||||
popup.insertAdjacentHTML('afterbegin', options.popupContent);
|
|
||||||
} else if (options.isHTML) {
|
|
||||||
popup.classList.add('AreaPopup');
|
|
||||||
popup.insertAdjacentHTML('afterbegin', options.label);
|
|
||||||
} else {
|
|
||||||
popup.textContent = options.label;
|
|
||||||
}
|
|
||||||
|
|
||||||
return popup;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.layers.set(id, area);
|
|
||||||
this.layerGroup.addLayer(area);
|
|
||||||
},
|
|
||||||
|
|
||||||
updateArea(id: string, options: DynmapArea, latLng: Function) {
|
|
||||||
let area = this.layers.get(id) as L.Polyline,
|
|
||||||
outline = (options.style && options.style.fillOpacity && (options.style.fillOpacity <= 0)) as boolean,
|
|
||||||
points = this.getPoints(options, latLng, outline);
|
|
||||||
|
|
||||||
if (!area) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
area.setLatLngs(points);
|
|
||||||
area.redraw();
|
|
||||||
},
|
|
||||||
|
|
||||||
getPoints(options: DynmapArea, latLng: Function, outline: boolean): LatLngExpression[] | LatLngExpression[][] {
|
|
||||||
if (options.x.length === 2) { /* Only 2 points */
|
|
||||||
if (options.y[0] === options.y[1]) {
|
|
||||||
return this.get2DBoxPoints(options, latLng, outline);
|
|
||||||
} else {
|
|
||||||
return this.get3DBoxPoints(options, latLng);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (options.y[0] === options.y[1]) {
|
|
||||||
return this.get2DShapePoints(options, latLng, outline);
|
|
||||||
} else {
|
|
||||||
return this.get3DShapePoints(options, latLng);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
get3DBoxPoints(options: DynmapArea, latLng: Function): LatLngExpression[][] {
|
|
||||||
const maxX = options.x[0],
|
|
||||||
minX = options.x[1],
|
|
||||||
maxY = options.y[0],
|
|
||||||
minY = options.y[1],
|
|
||||||
maxZ = options.z[0],
|
|
||||||
minZ = options.z[1];
|
|
||||||
|
|
||||||
return [
|
|
||||||
[
|
|
||||||
latLng(minX, minY, minZ),
|
|
||||||
latLng(maxX, minY, minZ),
|
|
||||||
latLng(maxX, minY, maxZ),
|
|
||||||
latLng(minX, minY, maxZ)
|
|
||||||
], [
|
|
||||||
latLng(minX, maxY, minZ),
|
|
||||||
latLng(maxX, maxY, minZ),
|
|
||||||
latLng(maxX, maxY, maxZ),
|
|
||||||
latLng(minX, maxY, maxZ)
|
|
||||||
], [
|
|
||||||
latLng(minX, minY, minZ),
|
|
||||||
latLng(minX, maxY, minZ),
|
|
||||||
latLng(maxX, maxY, minZ),
|
|
||||||
latLng(maxX, minY, minZ)
|
|
||||||
], [
|
|
||||||
latLng(maxX, minY, minZ),
|
|
||||||
latLng(maxX, maxY, minZ),
|
|
||||||
latLng(maxX, maxY, maxZ),
|
|
||||||
latLng(maxX, minY, maxZ)
|
|
||||||
], [
|
|
||||||
latLng(minX, minY, maxZ),
|
|
||||||
latLng(minX, maxY, maxZ),
|
|
||||||
latLng(maxX, maxY, maxZ),
|
|
||||||
latLng(maxX, minY, maxZ)
|
|
||||||
], [
|
|
||||||
latLng(minX, minY, minZ),
|
|
||||||
latLng(minX, maxY, minZ),
|
|
||||||
latLng(minX, maxY, maxZ),
|
|
||||||
latLng(minX, minY, maxZ)
|
|
||||||
]
|
|
||||||
];
|
|
||||||
},
|
|
||||||
|
|
||||||
get2DBoxPoints(options: DynmapArea, latLng: Function, outline: boolean): LatLngExpression[] {
|
|
||||||
const maxX = options.x[0],
|
|
||||||
minX = options.x[1],
|
|
||||||
minY = options.y[1],
|
|
||||||
maxZ = options.z[0],
|
|
||||||
minZ = options.z[1];
|
|
||||||
|
|
||||||
if (outline) {
|
|
||||||
return [
|
|
||||||
latLng(minX, minY, minZ),
|
|
||||||
latLng(maxX, minY, minZ),
|
|
||||||
latLng(maxX, minY, maxZ),
|
|
||||||
latLng(minX, minY, maxZ),
|
|
||||||
latLng(minX, minY, minZ)
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
return [
|
|
||||||
latLng(minX, minY, minZ),
|
|
||||||
latLng(maxX, minY, minZ),
|
|
||||||
latLng(maxX, minY, maxZ),
|
|
||||||
latLng(minX, minY, maxZ)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
get3DShapePoints(options: DynmapArea, latLng: Function): LatLngExpression[][] {
|
|
||||||
const toplist = [],
|
|
||||||
botlist = [],
|
|
||||||
polylist = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < options.x.length; i++) {
|
|
||||||
toplist[i] = latLng(options.x[i], options.y[0], options.z[i]);
|
|
||||||
botlist[i] = latLng(options.x[i], options.y[1], options.z[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < options.x.length; i++) {
|
|
||||||
const sidelist = [];
|
|
||||||
sidelist[0] = toplist[i];
|
|
||||||
sidelist[1] = botlist[i];
|
|
||||||
sidelist[2] = botlist[(i + 1) % options.x.length];
|
|
||||||
sidelist[3] = toplist[(i + 1) % options.x.length];
|
|
||||||
polylist[i] = sidelist;
|
|
||||||
}
|
|
||||||
|
|
||||||
polylist[options.x.length] = botlist;
|
|
||||||
polylist[options.x.length + 1] = toplist;
|
|
||||||
|
|
||||||
return polylist;
|
|
||||||
},
|
|
||||||
|
|
||||||
get2DShapePoints(options: DynmapArea, latLng: Function, outline: boolean): LatLngExpression[] {
|
|
||||||
const points = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < options.x.length; i++) {
|
|
||||||
points[i] = latLng(options.x[i], options.y[1], options.z[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outline) {
|
|
||||||
points.push(points[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return points;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
@ -1,140 +1,111 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, computed} from "@vue/runtime-core";
|
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
|
||||||
import L, {LatLngExpression} from 'leaflet';
|
import {Polyline, LayerGroup, Polygon} from 'leaflet';
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import {DynmapCircle} from "@/dynmap";
|
import {DynmapCircle, DynmapMarkerSet} from "@/dynmap";
|
||||||
|
import {ActionTypes} from "@/store/action-types";
|
||||||
|
import {createCircle, updateCircle} from "@/util/circles";
|
||||||
|
import Util from '@/util';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
circles: {
|
set: {
|
||||||
type: Object as () => Map<string, DynmapCircle>,
|
type: Object as () => DynmapMarkerSet,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
layerGroup: {
|
layerGroup: {
|
||||||
type: Object as () => L.LayerGroup,
|
type: Object as () => LayerGroup,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setup() {
|
setup(props) {
|
||||||
|
let updateFrame = 0;
|
||||||
|
|
||||||
const store = useStore(),
|
const store = useStore(),
|
||||||
currentProjection = computed(() => store.state.currentProjection),
|
currentProjection = computed(() => store.state.currentProjection),
|
||||||
layers = Object.freeze(new Map()) as Map<string, L.Path>;
|
pendingUpdates = computed(() => {
|
||||||
|
const markerSetUpdates = store.state.pendingSetUpdates.get(props.set.id);
|
||||||
|
|
||||||
return {
|
return markerSetUpdates && markerSetUpdates.circleUpdates.length;
|
||||||
layers,
|
}),
|
||||||
currentProjection,
|
layers = Object.freeze(new Map<string, Polyline | Polygon>()),
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
createCircles = () => {
|
||||||
//FIXME: Prevent unnecessary repositioning when changing worlds
|
const converter = Util.getPointConverter();
|
||||||
currentProjection() {
|
|
||||||
const projection = useStore().state.currentProjection,
|
props.set.circles.forEach((circle: DynmapCircle, id: string) => {
|
||||||
latLng = (x: number, y: number, z: number) => {
|
const layer = createCircle(circle, converter);
|
||||||
return projection.locationToLatLng({x, y, z});
|
|
||||||
|
layers.set(id, layer);
|
||||||
|
props.layerGroup.addLayer(layer);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteCircle = (id: string) => {
|
||||||
|
let circle = layers.get(id) as Polyline;
|
||||||
|
|
||||||
|
if (!circle) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
circle.remove();
|
||||||
for (const [id, circle] of this.circles) {
|
layers.delete(id);
|
||||||
this.updateCircle(id, circle, latLng);
|
},
|
||||||
|
|
||||||
|
handlePendingUpdates = () => {
|
||||||
|
useStore().dispatch(ActionTypes.POP_CIRCLE_UPDATES, {
|
||||||
|
markerSet: props.set.id,
|
||||||
|
amount: 10,
|
||||||
|
}).then(updates => {
|
||||||
|
const converter = Util.getPointConverter();
|
||||||
|
|
||||||
|
for(const update of updates) {
|
||||||
|
if(update.removed) {
|
||||||
|
console.log(`Deleting circle ${update.id}`);
|
||||||
|
deleteCircle(update.id);
|
||||||
|
} else {
|
||||||
|
console.log(`Updating/creating circle ${update.id}`);
|
||||||
|
const layer = updateCircle(layers.get(update.id), update.payload as DynmapCircle, converter)
|
||||||
|
|
||||||
|
if(!layers.has(update.id)) {
|
||||||
|
layers.set(update.id, layer);
|
||||||
|
props.layerGroup.addLayer(layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pendingUpdates.value) {
|
||||||
|
console.log('More updates left, scheduling frame');
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
|
||||||
|
} else {
|
||||||
|
updateFrame = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//FIXME: Prevent unnecessary repositioning when changing worlds
|
||||||
|
watch(currentProjection, () => {
|
||||||
|
const converter = Util.getPointConverter();
|
||||||
|
|
||||||
|
for (const [id, circle] of props.set.circles) {
|
||||||
|
updateCircle(layers.get(id), circle, converter);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
watch(pendingUpdates, (newValue, oldValue) => {
|
||||||
this.createCircles();
|
if(newValue && newValue > 0 && oldValue === 0 && !updateFrame) {
|
||||||
},
|
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
|
||||||
|
}
|
||||||
unmounted() {
|
});
|
||||||
|
|
||||||
|
onMounted(() => createCircles());
|
||||||
|
onUnmounted(() => updateFrame && cancelAnimationFrame(updateFrame));
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return null;
|
return null;
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
createCircles() {
|
|
||||||
const projection = useStore().state.currentProjection,
|
|
||||||
latLng = (x: number, y: number, z: number) => {
|
|
||||||
return projection.locationToLatLng({x, y, z});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.circles.forEach((circle: DynmapCircle, id: string) => {
|
|
||||||
this.createCircle(id, circle, latLng);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
createCircle(id: string, options: DynmapCircle, latLng: Function) {
|
|
||||||
const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0),
|
|
||||||
points = this.getPoints(options, latLng, outline);
|
|
||||||
let circle;
|
|
||||||
|
|
||||||
if(outline) {
|
|
||||||
circle = new L.Polyline(points, options.style);
|
|
||||||
} else {
|
|
||||||
circle = new L.Polygon(points, options.style);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(options.label) {
|
|
||||||
circle.bindPopup(() => {
|
|
||||||
const popup = document.createElement('span');
|
|
||||||
|
|
||||||
if (options.popupContent) {
|
|
||||||
popup.classList.add('CirclePopup');
|
|
||||||
popup.insertAdjacentHTML('afterbegin', options.popupContent);
|
|
||||||
} else if (options.isHTML) {
|
|
||||||
popup.classList.add('CirclePopup');
|
|
||||||
popup.insertAdjacentHTML('afterbegin', options.label);
|
|
||||||
} else {
|
|
||||||
popup.textContent = options.label;
|
|
||||||
}
|
|
||||||
|
|
||||||
return popup;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.layers.set(id, circle);
|
|
||||||
this.layerGroup.addLayer(circle);
|
|
||||||
},
|
|
||||||
|
|
||||||
getPoints(options: DynmapCircle, latLng: Function, outline: boolean): LatLngExpression[] {
|
|
||||||
const points = [];
|
|
||||||
|
|
||||||
for(let i = 0; i < 360; i++) {
|
|
||||||
const rad = i * Math.PI / 180.0,
|
|
||||||
x = options.radius[0] * Math.sin(rad) + options.location.x,
|
|
||||||
z = options.radius[1] * Math.cos(rad) + options.location.z;
|
|
||||||
|
|
||||||
console.log(x,options.location.y,z, latLng(x, options.location.y, z));
|
|
||||||
|
|
||||||
points.push(latLng(x, options.location.y, z));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(outline && points.length) {
|
|
||||||
points.push(points[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return points;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateCircle(id: string, options: DynmapCircle, latLng: Function) {
|
|
||||||
let circle = this.layers.get(id) as L.Polyline,
|
|
||||||
outline = (options.style && options.style.fillOpacity && (options.style.fillOpacity <= 0)) as boolean,
|
|
||||||
points = this.getPoints(options, latLng, outline);
|
|
||||||
|
|
||||||
if (!circle) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
circle.setLatLngs(points);
|
|
||||||
circle.redraw();
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
@ -1,122 +1,111 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, computed} from "@vue/runtime-core";
|
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
|
||||||
import L, {LatLngExpression} from 'leaflet';
|
import {Polyline, LayerGroup, Polygon} from 'leaflet';
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import {DynmapLine} from "@/dynmap";
|
import {DynmapLine, DynmapMarkerSet} from "@/dynmap";
|
||||||
|
import {ActionTypes} from "@/store/action-types";
|
||||||
|
import {createLine, updateLine} from "@/util/lines";
|
||||||
|
import Util from '@/util';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
lines: {
|
set: {
|
||||||
type: Object as () => Map<string, DynmapLine>,
|
type: Object as () => DynmapMarkerSet,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
layerGroup: {
|
layerGroup: {
|
||||||
type: Object as () => L.LayerGroup,
|
type: Object as () => LayerGroup,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setup() {
|
setup(props) {
|
||||||
|
let updateFrame = 0;
|
||||||
|
|
||||||
const store = useStore(),
|
const store = useStore(),
|
||||||
currentProjection = computed(() => store.state.currentProjection),
|
currentProjection = computed(() => store.state.currentProjection),
|
||||||
layers = Object.freeze(new Map()) as Map<string, L.Path>;
|
pendingUpdates = computed(() => {
|
||||||
|
const markerSetUpdates = store.state.pendingSetUpdates.get(props.set.id);
|
||||||
|
|
||||||
return {
|
return markerSetUpdates && markerSetUpdates.lineUpdates.length;
|
||||||
layers,
|
}),
|
||||||
currentProjection,
|
layers = Object.freeze(new Map<string, Polyline | Polygon>()),
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
createLines = () => {
|
||||||
//FIXME: Prevent unnecessary repositioning when changing worlds
|
const converter = Util.getPointConverter();
|
||||||
currentProjection() {
|
|
||||||
const projection = useStore().state.currentProjection,
|
props.set.lines.forEach((line: DynmapLine, id: string) => {
|
||||||
latLng = (x: number, y: number, z: number) => {
|
const layer = createLine(line, converter);
|
||||||
return projection.locationToLatLng({x, y, z});
|
|
||||||
|
layers.set(id, layer);
|
||||||
|
props.layerGroup.addLayer(layer);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteLine = (id: string) => {
|
||||||
|
let line = layers.get(id) as Polyline;
|
||||||
|
|
||||||
|
if (!line) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
line.remove();
|
||||||
for (const [id, line] of this.lines) {
|
layers.delete(id);
|
||||||
this.updateLine(id, line, latLng);
|
},
|
||||||
|
|
||||||
|
handlePendingUpdates = () => {
|
||||||
|
useStore().dispatch(ActionTypes.POP_LINE_UPDATES, {
|
||||||
|
markerSet: props.set.id,
|
||||||
|
amount: 10,
|
||||||
|
}).then(updates => {
|
||||||
|
const converter = Util.getPointConverter();
|
||||||
|
|
||||||
|
for(const update of updates) {
|
||||||
|
if(update.removed) {
|
||||||
|
console.log(`Deleting line ${update.id}`);
|
||||||
|
deleteLine(update.id);
|
||||||
|
} else {
|
||||||
|
console.log(`Updating/creating line ${update.id}`);
|
||||||
|
const layer = updateLine(layers.get(update.id), update.payload as DynmapLine, converter)
|
||||||
|
|
||||||
|
if(!layers.has(update.id)) {
|
||||||
|
layers.set(update.id, layer);
|
||||||
|
props.layerGroup.addLayer(layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pendingUpdates.value) {
|
||||||
|
console.log('More updates left, scheduling frame');
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
|
||||||
|
} else {
|
||||||
|
updateFrame = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//FIXME: Prevent unnecessary repositioning when changing worlds
|
||||||
|
watch(currentProjection, () => {
|
||||||
|
const converter = Util.getPointConverter();
|
||||||
|
|
||||||
|
for (const [id, line] of props.set.lines) {
|
||||||
|
updateLine(layers.get(id), line, converter);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
watch(pendingUpdates, (newValue, oldValue) => {
|
||||||
this.createLines();
|
if(newValue && newValue > 0 && oldValue === 0 && !updateFrame) {
|
||||||
},
|
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
|
||||||
|
}
|
||||||
unmounted() {
|
});
|
||||||
|
|
||||||
|
onMounted(() => createLines());
|
||||||
|
onUnmounted(() => updateFrame && cancelAnimationFrame(updateFrame));
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return null;
|
return null;
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
createLines() {
|
|
||||||
const projection = useStore().state.currentProjection,
|
|
||||||
latLng = (x: number, y: number, z: number) => {
|
|
||||||
return projection.locationToLatLng({x, y, z});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.lines.forEach((line: DynmapLine, id: string) => {
|
|
||||||
this.createLine(id, line, latLng);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
createLine(id: string, options: DynmapLine, latLng: Function) {
|
|
||||||
const points = this.getPoints(options, latLng),
|
|
||||||
line= new L.Polyline(points, options.style);
|
|
||||||
|
|
||||||
if(options.label) {
|
|
||||||
line.bindPopup(() => {
|
|
||||||
const popup = document.createElement('span');
|
|
||||||
|
|
||||||
if (options.popupContent) {
|
|
||||||
popup.classList.add('LinePopup');
|
|
||||||
popup.insertAdjacentHTML('afterbegin', options.popupContent);
|
|
||||||
} else if (options.isHTML) {
|
|
||||||
popup.classList.add('LinePopup');
|
|
||||||
popup.insertAdjacentHTML('afterbegin', options.label);
|
|
||||||
} else {
|
|
||||||
popup.textContent = options.label;
|
|
||||||
}
|
|
||||||
|
|
||||||
return popup;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.layers.set(id, line);
|
|
||||||
this.layerGroup.addLayer(line);
|
|
||||||
},
|
|
||||||
|
|
||||||
getPoints(options: DynmapLine, latLng: Function): LatLngExpression[] {
|
|
||||||
const points = [];
|
|
||||||
|
|
||||||
for(let i = 0; i < options.x.length; i++) {
|
|
||||||
points.push(latLng(options.x[i], options.y[i], options.z[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return points;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateLine(id: string, options: DynmapLine, latLng: Function) {
|
|
||||||
let line = this.layers.get(id) as L.Polyline,
|
|
||||||
points = this.getPoints(options, latLng);
|
|
||||||
|
|
||||||
if (!line) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
line.setLatLngs(points);
|
|
||||||
line.redraw();
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
38
src/dynmap.d.ts
vendored
38
src/dynmap.d.ts
vendored
@ -139,8 +139,8 @@ interface DynmapUpdateResponse {
|
|||||||
configHash: number;
|
configHash: number;
|
||||||
playerCount: number;
|
playerCount: number;
|
||||||
players: Set<DynmapPlayer>;
|
players: Set<DynmapPlayer>;
|
||||||
|
updates: DynmapUpdates;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
//TODO: Tiles etc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DynmapPlayer {
|
interface DynmapPlayer {
|
||||||
@ -153,6 +153,7 @@ interface DynmapPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface DynmapMarkerSet {
|
interface DynmapMarkerSet {
|
||||||
|
id: string,
|
||||||
label: string;
|
label: string;
|
||||||
hidden: boolean;
|
hidden: boolean;
|
||||||
priority: number;
|
priority: number;
|
||||||
@ -210,3 +211,38 @@ interface DynmapCircle {
|
|||||||
maxZoom?: number;
|
maxZoom?: number;
|
||||||
popupContent?: string;
|
popupContent?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DynmapUpdates {
|
||||||
|
markerSets: Map<string, DynmapMarkerSetUpdates>,
|
||||||
|
tiles: Map<string, number>,
|
||||||
|
chat: Array<any> //TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DynmapMarkerSetUpdates {
|
||||||
|
markerUpdates: Array<DynmapMarkerUpdate>
|
||||||
|
areaUpdates: Array<DynmapAreaUpdate>
|
||||||
|
circleUpdates: Array<DynmapCircleUpdate>
|
||||||
|
lineUpdates: Array<DynmapLineUpdate>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DynmapUpdate {
|
||||||
|
id: string,
|
||||||
|
removed: boolean,
|
||||||
|
payload?: any,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DynmapMarkerUpdate extends DynmapUpdate {
|
||||||
|
payload?: DynmapMarker
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DynmapAreaUpdate extends DynmapUpdate {
|
||||||
|
payload?: DynmapArea
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DynmapCircleUpdate extends DynmapUpdate {
|
||||||
|
payload?: DynmapCircle
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DynmapLineUpdate extends DynmapUpdate {
|
||||||
|
payload?: DynmapLine
|
||||||
|
}
|
@ -3,4 +3,8 @@ export enum ActionTypes {
|
|||||||
GET_UPDATE = "getUpdate",
|
GET_UPDATE = "getUpdate",
|
||||||
GET_MARKER_SETS = "getMarkerSets",
|
GET_MARKER_SETS = "getMarkerSets",
|
||||||
SET_PLAYERS = "setPlayers",
|
SET_PLAYERS = "setPlayers",
|
||||||
|
POP_MARKER_UPDATES = "popMarkerUpdates",
|
||||||
|
POP_AREA_UPDATES = "popAreaUpdates",
|
||||||
|
POP_CIRCLE_UPDATES = "popCircleUpdates",
|
||||||
|
POP_LINE_UPDATES = "popLineUpdates",
|
||||||
}
|
}
|
@ -4,7 +4,14 @@ import {State} from "@/store/state";
|
|||||||
import {ActionTypes} from "@/store/action-types";
|
import {ActionTypes} from "@/store/action-types";
|
||||||
import API from '@/api';
|
import API from '@/api';
|
||||||
import {Mutations} from "@/store/mutations";
|
import {Mutations} from "@/store/mutations";
|
||||||
import {DynmapConfigurationResponse, DynmapMarkerSet, DynmapPlayer, DynmapUpdateResponse} from "@/dynmap";
|
import {
|
||||||
|
DynmapAreaUpdate, DynmapCircleUpdate,
|
||||||
|
DynmapConfigurationResponse, DynmapLineUpdate,
|
||||||
|
DynmapMarkerSet,
|
||||||
|
DynmapMarkerUpdate,
|
||||||
|
DynmapPlayer,
|
||||||
|
DynmapUpdateResponse
|
||||||
|
} from "@/dynmap";
|
||||||
|
|
||||||
type AugmentedActionContext = {
|
type AugmentedActionContext = {
|
||||||
commit<K extends keyof Mutations>(
|
commit<K extends keyof Mutations>(
|
||||||
@ -27,6 +34,22 @@ export interface Actions {
|
|||||||
{commit}: AugmentedActionContext,
|
{commit}: AugmentedActionContext,
|
||||||
payload: Set<DynmapPlayer>
|
payload: Set<DynmapPlayer>
|
||||||
):Promise<Map<string, DynmapMarkerSet>>
|
):Promise<Map<string, DynmapMarkerSet>>
|
||||||
|
[ActionTypes.POP_MARKER_UPDATES](
|
||||||
|
{commit}: AugmentedActionContext,
|
||||||
|
payload: {markerSet: string, amount: number}
|
||||||
|
): Promise<DynmapMarkerUpdate[]>
|
||||||
|
[ActionTypes.POP_AREA_UPDATES](
|
||||||
|
{commit}: AugmentedActionContext,
|
||||||
|
payload: {markerSet: string, amount: number}
|
||||||
|
): Promise<DynmapAreaUpdate[]>
|
||||||
|
[ActionTypes.POP_CIRCLE_UPDATES](
|
||||||
|
{commit}: AugmentedActionContext,
|
||||||
|
payload: {markerSet: string, amount: number}
|
||||||
|
): Promise<DynmapCircleUpdate[]>
|
||||||
|
[ActionTypes.POP_LINE_UPDATES](
|
||||||
|
{commit}: AugmentedActionContext,
|
||||||
|
payload: {markerSet: string, amount: number}
|
||||||
|
): Promise<DynmapLineUpdate[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const actions: ActionTree<State, State> & Actions = {
|
export const actions: ActionTree<State, State> & Actions = {
|
||||||
@ -52,10 +75,11 @@ export const actions: ActionTree<State, State> & Actions = {
|
|||||||
return Promise.reject("No current world");
|
return Promise.reject("No current world");
|
||||||
}
|
}
|
||||||
|
|
||||||
return API.getUpdate(state.updateRequestId, state.currentWorld.name, state.updateTimestamp.getUTCMilliseconds()).then(update => {
|
return API.getUpdate(state.updateRequestId, state.currentWorld.name, state.updateTimestamp.valueOf()).then(update => {
|
||||||
commit(MutationTypes.SET_WORLD_STATE, update.worldState);
|
commit(MutationTypes.SET_WORLD_STATE, update.worldState);
|
||||||
commit(MutationTypes.SET_UPDATE_TIMESTAMP, new Date(update.timestamp));
|
commit(MutationTypes.SET_UPDATE_TIMESTAMP, new Date(update.timestamp));
|
||||||
commit(MutationTypes.INCREMENT_REQUEST_ID, undefined);
|
commit(MutationTypes.INCREMENT_REQUEST_ID, undefined);
|
||||||
|
commit(MutationTypes.ADD_MARKER_SET_UPDATES, update.updates.markerSets);
|
||||||
|
|
||||||
return dispatch(ActionTypes.SET_PLAYERS, update.players).then(() => {
|
return dispatch(ActionTypes.SET_PLAYERS, update.players).then(() => {
|
||||||
return update;
|
return update;
|
||||||
@ -97,5 +121,58 @@ export const actions: ActionTree<State, State> & Actions = {
|
|||||||
|
|
||||||
return markerSets;
|
return markerSets;
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
|
[ActionTypes.POP_MARKER_UPDATES]({commit, state}, {markerSet, amount}: {markerSet: string, amount: number}): Promise<DynmapMarkerUpdate[]> {
|
||||||
|
if(!state.markerSets.has(markerSet)) {
|
||||||
|
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updates = state.pendingSetUpdates.get(markerSet)!.markerUpdates.slice(0, amount);
|
||||||
|
|
||||||
|
commit(MutationTypes.POP_MARKER_UPDATES, {markerSet, amount});
|
||||||
|
|
||||||
|
return Promise.resolve(updates);
|
||||||
|
},
|
||||||
|
|
||||||
|
[ActionTypes.POP_AREA_UPDATES]({commit, state}, {markerSet, amount}: {markerSet: string, amount: number}): Promise<DynmapAreaUpdate[]> {
|
||||||
|
if(!state.markerSets.has(markerSet)) {
|
||||||
|
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updates = state.pendingSetUpdates.get(markerSet)!.areaUpdates.slice(0, amount);
|
||||||
|
|
||||||
|
commit(MutationTypes.POP_AREA_UPDATES, {markerSet, amount});
|
||||||
|
|
||||||
|
return Promise.resolve(updates);
|
||||||
|
},
|
||||||
|
|
||||||
|
[ActionTypes.POP_CIRCLE_UPDATES]({commit, state}, {markerSet, amount}: {markerSet: string, amount: number}): Promise<DynmapCircleUpdate[]> {
|
||||||
|
if(!state.markerSets.has(markerSet)) {
|
||||||
|
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updates = state.pendingSetUpdates.get(markerSet)!.circleUpdates.slice(0, amount);
|
||||||
|
|
||||||
|
commit(MutationTypes.POP_CIRCLE_UPDATES, {markerSet, amount});
|
||||||
|
|
||||||
|
return Promise.resolve(updates);
|
||||||
|
},
|
||||||
|
|
||||||
|
[ActionTypes.POP_LINE_UPDATES]({commit, state}, {markerSet, amount}: {markerSet: string, amount: number}): Promise<DynmapLineUpdate[]> {
|
||||||
|
if(!state.markerSets.has(markerSet)) {
|
||||||
|
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updates = state.pendingSetUpdates.get(markerSet)!.lineUpdates.slice(0, amount);
|
||||||
|
|
||||||
|
commit(MutationTypes.POP_LINE_UPDATES, {markerSet, amount});
|
||||||
|
|
||||||
|
return Promise.resolve(updates);
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
@ -7,6 +7,11 @@ export enum MutationTypes {
|
|||||||
ADD_WORLD = 'addWorld',
|
ADD_WORLD = 'addWorld',
|
||||||
SET_WORLD_STATE = 'setWorldState',
|
SET_WORLD_STATE = 'setWorldState',
|
||||||
SET_UPDATE_TIMESTAMP = 'setUpdateTimestamp',
|
SET_UPDATE_TIMESTAMP = 'setUpdateTimestamp',
|
||||||
|
ADD_MARKER_SET_UPDATES = 'addMarkerSetUpdates',
|
||||||
|
POP_MARKER_UPDATES = 'popMarkerUpdates',
|
||||||
|
POP_AREA_UPDATES = 'popAreaUpdates',
|
||||||
|
POP_CIRCLE_UPDATES = 'popCircleUpdates',
|
||||||
|
POP_LINE_UPDATES = 'popLineUpdates',
|
||||||
INCREMENT_REQUEST_ID = 'incrementRequestId',
|
INCREMENT_REQUEST_ID = 'incrementRequestId',
|
||||||
SET_PLAYERS = 'setPlayers',
|
SET_PLAYERS = 'setPlayers',
|
||||||
SET_PLAYERS_ASYNC = 'setPlayersAsync',
|
SET_PLAYERS_ASYNC = 'setPlayersAsync',
|
||||||
|
@ -2,7 +2,17 @@ import {MutationTree} from "vuex";
|
|||||||
import {MutationTypes} from "@/store/mutation-types";
|
import {MutationTypes} from "@/store/mutation-types";
|
||||||
import {State} from "@/store/state";
|
import {State} from "@/store/state";
|
||||||
import {
|
import {
|
||||||
DynmapComponentConfig, DynmapMarkerSet,
|
DynmapArea,
|
||||||
|
DynmapAreaUpdate,
|
||||||
|
DynmapCircle,
|
||||||
|
DynmapCircleUpdate,
|
||||||
|
DynmapComponentConfig,
|
||||||
|
DynmapLine,
|
||||||
|
DynmapLineUpdate,
|
||||||
|
DynmapMarker,
|
||||||
|
DynmapMarkerSet,
|
||||||
|
DynmapMarkerSetUpdates,
|
||||||
|
DynmapMarkerUpdate,
|
||||||
DynmapMessageConfig,
|
DynmapMessageConfig,
|
||||||
DynmapPlayer,
|
DynmapPlayer,
|
||||||
DynmapServerConfig,
|
DynmapServerConfig,
|
||||||
@ -25,9 +35,15 @@ export type Mutations<S = State> = {
|
|||||||
[MutationTypes.ADD_WORLD](state: S, world: DynmapWorld): void
|
[MutationTypes.ADD_WORLD](state: S, world: DynmapWorld): void
|
||||||
[MutationTypes.SET_WORLD_STATE](state: S, worldState: DynmapWorldState): void
|
[MutationTypes.SET_WORLD_STATE](state: S, worldState: DynmapWorldState): void
|
||||||
[MutationTypes.SET_UPDATE_TIMESTAMP](state: S, time: Date): void
|
[MutationTypes.SET_UPDATE_TIMESTAMP](state: S, time: Date): void
|
||||||
|
[MutationTypes.ADD_MARKER_SET_UPDATES](state: S, updates: Map<string, DynmapMarkerSetUpdates>): void
|
||||||
|
|
||||||
|
[MutationTypes.POP_MARKER_UPDATES](state: S, payload: {markerSet: string, amount: number}): Array<DynmapMarkerUpdate>
|
||||||
|
[MutationTypes.POP_AREA_UPDATES](state: S, payload: {markerSet: string, amount: number}): Array<DynmapAreaUpdate>
|
||||||
|
[MutationTypes.POP_CIRCLE_UPDATES](state: S, payload: {markerSet: string, amount: number}): Array<DynmapCircleUpdate>
|
||||||
|
[MutationTypes.POP_LINE_UPDATES](state: S, payload: {markerSet: string, amount: number}): Array<DynmapLineUpdate>
|
||||||
|
|
||||||
[MutationTypes.INCREMENT_REQUEST_ID](state: S): void
|
[MutationTypes.INCREMENT_REQUEST_ID](state: S): void
|
||||||
// [MutationTypes.SET_PLAYERS](state: S, players: Array<DynmapPlayer>): void
|
[MutationTypes.SET_PLAYERS_ASYNC](state: S, players: Set<DynmapPlayer>): Set<DynmapPlayer>
|
||||||
[MutationTypes.SET_PLAYERS_ASYNC](state: S, players: Set<DynmapPlayer>): void
|
|
||||||
[MutationTypes.SYNC_PLAYERS](state: S, keep: Set<string>): void
|
[MutationTypes.SYNC_PLAYERS](state: S, keep: Set<string>): void
|
||||||
[MutationTypes.SET_CURRENT_MAP](state: S, payload: CurrentMapPayload): void
|
[MutationTypes.SET_CURRENT_MAP](state: S, payload: CurrentMapPayload): void
|
||||||
[MutationTypes.SET_CURRENT_PROJECTION](state: S, payload: DynmapProjection): void
|
[MutationTypes.SET_CURRENT_PROJECTION](state: S, payload: DynmapProjection): void
|
||||||
@ -73,6 +89,16 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
//Sets the existing marker sets from the last marker fetch
|
//Sets the existing marker sets from the last marker fetch
|
||||||
[MutationTypes.SET_MARKER_SETS](state: State, markerSets: Map<string, DynmapMarkerSet>) {
|
[MutationTypes.SET_MARKER_SETS](state: State, markerSets: Map<string, DynmapMarkerSet>) {
|
||||||
state.markerSets = markerSets;
|
state.markerSets = markerSets;
|
||||||
|
state.pendingSetUpdates.clear();
|
||||||
|
|
||||||
|
for(const entry of markerSets) {
|
||||||
|
state.pendingSetUpdates.set(entry[0], {
|
||||||
|
markerUpdates: [],
|
||||||
|
areaUpdates: [],
|
||||||
|
circleUpdates: [],
|
||||||
|
lineUpdates: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
[MutationTypes.ADD_WORLD](state: State, world: DynmapWorld) {
|
[MutationTypes.ADD_WORLD](state: State, world: DynmapWorld) {
|
||||||
@ -89,46 +115,101 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
state.updateTimestamp = timestamp;
|
state.updateTimestamp = timestamp;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
//Sets the timestamp of the last update fetch
|
||||||
|
[MutationTypes.ADD_MARKER_SET_UPDATES](state: State, updates: Map<string, DynmapMarkerSetUpdates>) {
|
||||||
|
for(const entry of updates) {
|
||||||
|
if(!state.markerSets.has(entry[0])) {
|
||||||
|
console.log(`Marker set ${entry[0]} doesn't exist`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const set = state.markerSets.get(entry[0]) as DynmapMarkerSet,
|
||||||
|
setUpdates = state.pendingSetUpdates.get(entry[0]) as DynmapMarkerSetUpdates;
|
||||||
|
|
||||||
|
//Update non-reactive lists
|
||||||
|
for(const update of entry[1].markerUpdates) {
|
||||||
|
if(update.removed) {
|
||||||
|
set.markers.delete(update.id);
|
||||||
|
} else {
|
||||||
|
set.markers.set(update.id, update.payload as DynmapMarker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const update of entry[1].areaUpdates) {
|
||||||
|
if(update.removed) {
|
||||||
|
set.areas.delete(update.id);
|
||||||
|
} else {
|
||||||
|
set.areas.set(update.id, update.payload as DynmapArea);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const update of entry[1].circleUpdates) {
|
||||||
|
if(update.removed) {
|
||||||
|
set.circles.delete(update.id);
|
||||||
|
} else {
|
||||||
|
set.circles.set(update.id, update.payload as DynmapCircle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const update of entry[1].lineUpdates) {
|
||||||
|
if(update.removed) {
|
||||||
|
set.lines.delete(update.id);
|
||||||
|
} else {
|
||||||
|
set.lines.set(update.id, update.payload as DynmapLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add to reactive pending updates lists
|
||||||
|
setUpdates.markerUpdates = setUpdates.markerUpdates.concat(entry[1].markerUpdates);
|
||||||
|
setUpdates.areaUpdates = setUpdates.areaUpdates.concat(entry[1].areaUpdates);
|
||||||
|
setUpdates.circleUpdates = setUpdates.circleUpdates.concat(entry[1].circleUpdates);
|
||||||
|
setUpdates.lineUpdates = setUpdates.lineUpdates.concat(entry[1].lineUpdates);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
[MutationTypes.POP_MARKER_UPDATES](state: State, {markerSet, amount}): Array<DynmapMarkerUpdate> {
|
||||||
|
if(!state.markerSets.has(markerSet)) {
|
||||||
|
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.pendingSetUpdates.get(markerSet)!.markerUpdates.splice(0, amount);
|
||||||
|
},
|
||||||
|
|
||||||
|
[MutationTypes.POP_AREA_UPDATES](state: State, {markerSet, amount}): Array<DynmapAreaUpdate> {
|
||||||
|
if(!state.markerSets.has(markerSet)) {
|
||||||
|
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.pendingSetUpdates.get(markerSet)!.areaUpdates.splice(0, amount);
|
||||||
|
},
|
||||||
|
|
||||||
|
[MutationTypes.POP_CIRCLE_UPDATES](state: State, {markerSet, amount}): Array<DynmapCircleUpdate> {
|
||||||
|
if(!state.markerSets.has(markerSet)) {
|
||||||
|
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.pendingSetUpdates.get(markerSet)!.circleUpdates.splice(0, amount);
|
||||||
|
},
|
||||||
|
|
||||||
|
[MutationTypes.POP_LINE_UPDATES](state: State, {markerSet, amount}): Array<DynmapLineUpdate> {
|
||||||
|
if(!state.markerSets.has(markerSet)) {
|
||||||
|
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.pendingSetUpdates.get(markerSet)!.lineUpdates.splice(0, amount);
|
||||||
|
},
|
||||||
|
|
||||||
//Increments the request id for the next update fetch
|
//Increments the request id for the next update fetch
|
||||||
[MutationTypes.INCREMENT_REQUEST_ID](state: State) {
|
[MutationTypes.INCREMENT_REQUEST_ID](state: State) {
|
||||||
state.updateRequestId++;
|
state.updateRequestId++;
|
||||||
},
|
},
|
||||||
|
|
||||||
// [MutationTypes.SET_PLAYERS](state: State, players: Array<DynmapPlayer>) {
|
// Set up to 10 players at once
|
||||||
// const existingPlayers: Set<string> = new Set();
|
[MutationTypes.SET_PLAYERS_ASYNC](state: State, players: Set<DynmapPlayer>): Set<DynmapPlayer> {
|
||||||
//
|
|
||||||
// players.forEach(player => {
|
|
||||||
// existingPlayers.add(player.account);
|
|
||||||
//
|
|
||||||
// if (state.players.has(player.account)) {
|
|
||||||
// const existing = state.players.get(player.account);
|
|
||||||
//
|
|
||||||
// existing!.health = player.health;
|
|
||||||
// existing!.armor = player.armor;
|
|
||||||
// existing!.location = Object.assign(existing!.location, player.location);
|
|
||||||
// existing!.name = player.name;
|
|
||||||
// existing!.sort = player.sort;
|
|
||||||
// } else {
|
|
||||||
// state.players.set(player.account, {
|
|
||||||
// account: player.account,
|
|
||||||
// health: player.health,
|
|
||||||
// armor: player.armor,
|
|
||||||
// location: player.location,
|
|
||||||
// name: player.name,
|
|
||||||
// sort: player.sort,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// for (const key of state.players.keys()) {
|
|
||||||
// if (!existingPlayers.has(key)) {
|
|
||||||
// state.players.delete(key);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
|
|
||||||
//Set up to 10 players at once, returning the rest for future setting
|
|
||||||
[MutationTypes.SET_PLAYERS_ASYNC](state: State, players: Set<DynmapPlayer>) {
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
for(const player of players) {
|
for(const player of players) {
|
||||||
@ -154,9 +235,11 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
players.delete(player);
|
players.delete(player);
|
||||||
|
|
||||||
if(++count >= 10) {
|
if(++count >= 10) {
|
||||||
return players;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return players;
|
||||||
},
|
},
|
||||||
|
|
||||||
//Removes all players not found in the provided keep set
|
//Removes all players not found in the provided keep set
|
||||||
@ -184,6 +267,8 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
if(state.currentWorld !== newWorld) {
|
if(state.currentWorld !== newWorld) {
|
||||||
state.currentWorld = state.worlds.get(worldName);
|
state.currentWorld = state.worlds.get(worldName);
|
||||||
state.markerSets.clear();
|
state.markerSets.clear();
|
||||||
|
state.pendingSetUpdates.clear();
|
||||||
|
state.pendingTileUpdates = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
state.currentMap = state.maps.get(mapName);
|
state.currentMap = state.maps.get(mapName);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
DynmapComponentConfig,
|
DynmapComponentConfig,
|
||||||
DynmapMap, DynmapMarker, DynmapMarkerSet,
|
DynmapMap, DynmapMarkerSet, DynmapMarkerSetUpdates,
|
||||||
DynmapMessageConfig,
|
DynmapMessageConfig,
|
||||||
DynmapPlayer,
|
DynmapPlayer,
|
||||||
DynmapServerConfig,
|
DynmapServerConfig,
|
||||||
@ -18,9 +18,11 @@ export type State = {
|
|||||||
players: Map<string, DynmapPlayer>;
|
players: Map<string, DynmapPlayer>;
|
||||||
markerSets: Map<string, DynmapMarkerSet>;
|
markerSets: Map<string, DynmapMarkerSet>;
|
||||||
|
|
||||||
|
pendingSetUpdates: Map<string, DynmapMarkerSetUpdates>;
|
||||||
|
pendingTileUpdates: Array<string>;
|
||||||
|
|
||||||
following?: DynmapPlayer;
|
following?: DynmapPlayer;
|
||||||
|
|
||||||
// currentServer?: string;
|
|
||||||
currentWorldState: DynmapWorldState;
|
currentWorldState: DynmapWorldState;
|
||||||
currentWorld?: DynmapWorld;
|
currentWorld?: DynmapWorld;
|
||||||
currentMap?: DynmapMap;
|
currentMap?: DynmapMap;
|
||||||
@ -65,7 +67,10 @@ export const state: State = {
|
|||||||
worlds: new Map(), //Defined (loaded) worlds with maps from configuration.json
|
worlds: new Map(), //Defined (loaded) worlds with maps from configuration.json
|
||||||
maps: new Map(), //Defined maps from configuration.json
|
maps: new Map(), //Defined maps from configuration.json
|
||||||
players: new Map(), //Online players from world.json
|
players: new Map(), //Online players from world.json
|
||||||
|
|
||||||
markerSets: new Map(), //Markers from world_markers.json. Contents of each set isn't reactive for performance reasons.
|
markerSets: new Map(), //Markers from world_markers.json. Contents of each set isn't reactive for performance reasons.
|
||||||
|
pendingSetUpdates: new Map(), //Pending updates to markers/areas/etc for each marker set
|
||||||
|
pendingTileUpdates: [], //Pending updates to map tiles
|
||||||
|
|
||||||
//Dynmap optional components
|
//Dynmap optional components
|
||||||
components: {
|
components: {
|
||||||
@ -73,6 +78,7 @@ export const state: State = {
|
|||||||
markers: {
|
markers: {
|
||||||
showLabels: false,
|
showLabels: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Optional "playermarkers" component. Settings for online player markers.
|
// Optional "playermarkers" component. Settings for online player markers.
|
||||||
// If not present, player markers will be disabled
|
// If not present, player markers will be disabled
|
||||||
playerMarkers: undefined,
|
playerMarkers: undefined,
|
||||||
@ -83,22 +89,23 @@ export const state: State = {
|
|||||||
//Optional clock component. Used for both "digitalclock" and "timeofdayclock". Shows world time/weather.
|
//Optional clock component. Used for both "digitalclock" and "timeofdayclock". Shows world time/weather.
|
||||||
clockControl: undefined,
|
clockControl: undefined,
|
||||||
|
|
||||||
//Optional "link" component. Adds button to get url for current position
|
//Optional "link" component. Adds button to copy url for current position
|
||||||
linkControl: false,
|
linkControl: false,
|
||||||
|
|
||||||
|
//Optional "logo" controls.
|
||||||
logoControls: [],
|
logoControls: [],
|
||||||
},
|
},
|
||||||
|
|
||||||
following: undefined,
|
following: undefined,
|
||||||
|
|
||||||
currentWorld: undefined,
|
currentWorld: undefined,
|
||||||
|
currentMap: undefined,
|
||||||
|
currentProjection: new DynmapProjection(), //Projection for converting location <-> latlg. Object itself isn't reactive for performance reasons
|
||||||
currentWorldState: {
|
currentWorldState: {
|
||||||
raining: false,
|
raining: false,
|
||||||
thundering: false,
|
thundering: false,
|
||||||
timeOfDay: 0,
|
timeOfDay: 0,
|
||||||
},
|
},
|
||||||
currentMap: undefined,
|
|
||||||
currentProjection: new DynmapProjection(), //Projection for converting location <-> latlg. Object itself isn't reactive for performance reasons
|
|
||||||
|
|
||||||
updateRequestId: 0,
|
updateRequestId: 0,
|
||||||
updateTimestamp: new Date(),
|
updateTimestamp: new Date(),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import {DynmapPlayer} from "@/dynmap";
|
import {DynmapPlayer} from "@/dynmap";
|
||||||
|
import {useStore} from "@/store";
|
||||||
|
|
||||||
const headCache = new Map<DynmapPlayer, HTMLImageElement>();
|
const headCache = new Map<DynmapPlayer, HTMLImageElement>();
|
||||||
|
|
||||||
@ -49,5 +50,13 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return base + addition;
|
return base + addition;
|
||||||
|
},
|
||||||
|
|
||||||
|
getPointConverter() {
|
||||||
|
const projection = useStore().state.currentProjection;
|
||||||
|
|
||||||
|
return (x: number, y: number, z: number) => {
|
||||||
|
return projection.locationToLatLng({x, y, z});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
170
src/util/areas.ts
Normal file
170
src/util/areas.ts
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
import {LatLngExpression, Polygon, Polyline} from "leaflet";
|
||||||
|
import {DynmapArea} from "@/dynmap";
|
||||||
|
|
||||||
|
export const createArea = (options: DynmapArea, converter: Function): Polyline | Polygon => {
|
||||||
|
const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0),
|
||||||
|
points = getPoints(options, converter, outline),
|
||||||
|
area = outline ? new Polyline(points, options.style) : new Polygon(points, options.style);
|
||||||
|
|
||||||
|
if (options.label) {
|
||||||
|
area.bindPopup(() => createPopup(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
return area;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateArea = (area: Polyline | Polygon | undefined, options: DynmapArea, converter: Function): Polyline | Polygon => {
|
||||||
|
const outline = (options.style && options.style.fillOpacity && (options.style.fillOpacity <= 0)) as boolean,
|
||||||
|
points = getPoints(options, converter, outline);
|
||||||
|
|
||||||
|
if (!area) {
|
||||||
|
return createArea(options, converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
area.unbindPopup();
|
||||||
|
area.bindPopup(() => createPopup(options));
|
||||||
|
area.setStyle(options.style);
|
||||||
|
area.setLatLngs(points);
|
||||||
|
area.redraw();
|
||||||
|
|
||||||
|
return area;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createPopup = (options: DynmapArea): HTMLElement => {
|
||||||
|
const popup = document.createElement('span');
|
||||||
|
|
||||||
|
if (options.popupContent) {
|
||||||
|
popup.classList.add('AreaPopup');
|
||||||
|
popup.insertAdjacentHTML('afterbegin', options.popupContent);
|
||||||
|
} else if (options.isHTML) {
|
||||||
|
popup.classList.add('AreaPopup');
|
||||||
|
popup.insertAdjacentHTML('afterbegin', options.label);
|
||||||
|
} else {
|
||||||
|
popup.textContent = options.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
return popup;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPoints = (options: DynmapArea, converter: Function, outline: boolean): LatLngExpression[] | LatLngExpression[][] => {
|
||||||
|
if (options.x.length === 2) { /* Only 2 points */
|
||||||
|
if (options.y[0] === options.y[1]) {
|
||||||
|
return get2DBoxPoints(options, converter, outline);
|
||||||
|
} else {
|
||||||
|
return get3DBoxPoints(options, converter);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (options.y[0] === options.y[1]) {
|
||||||
|
return get2DShapePoints(options, converter, outline);
|
||||||
|
} else {
|
||||||
|
return get3DShapePoints(options, converter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const get3DBoxPoints = (options: DynmapArea, converter: Function): LatLngExpression[][] => {
|
||||||
|
const maxX = options.x[0],
|
||||||
|
minX = options.x[1],
|
||||||
|
maxY = options.y[0],
|
||||||
|
minY = options.y[1],
|
||||||
|
maxZ = options.z[0],
|
||||||
|
minZ = options.z[1];
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
converter(minX, minY, minZ),
|
||||||
|
converter(maxX, minY, minZ),
|
||||||
|
converter(maxX, minY, maxZ),
|
||||||
|
converter(minX, minY, maxZ)
|
||||||
|
], [
|
||||||
|
converter(minX, maxY, minZ),
|
||||||
|
converter(maxX, maxY, minZ),
|
||||||
|
converter(maxX, maxY, maxZ),
|
||||||
|
converter(minX, maxY, maxZ)
|
||||||
|
], [
|
||||||
|
converter(minX, minY, minZ),
|
||||||
|
converter(minX, maxY, minZ),
|
||||||
|
converter(maxX, maxY, minZ),
|
||||||
|
converter(maxX, minY, minZ)
|
||||||
|
], [
|
||||||
|
converter(maxX, minY, minZ),
|
||||||
|
converter(maxX, maxY, minZ),
|
||||||
|
converter(maxX, maxY, maxZ),
|
||||||
|
converter(maxX, minY, maxZ)
|
||||||
|
], [
|
||||||
|
converter(minX, minY, maxZ),
|
||||||
|
converter(minX, maxY, maxZ),
|
||||||
|
converter(maxX, maxY, maxZ),
|
||||||
|
converter(maxX, minY, maxZ)
|
||||||
|
], [
|
||||||
|
converter(minX, minY, minZ),
|
||||||
|
converter(minX, maxY, minZ),
|
||||||
|
converter(minX, maxY, maxZ),
|
||||||
|
converter(minX, minY, maxZ)
|
||||||
|
]
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const get2DBoxPoints = (options: DynmapArea, converter: Function, outline: boolean): LatLngExpression[] => {
|
||||||
|
const maxX = options.x[0],
|
||||||
|
minX = options.x[1],
|
||||||
|
minY = options.y[1],
|
||||||
|
maxZ = options.z[0],
|
||||||
|
minZ = options.z[1];
|
||||||
|
|
||||||
|
if (outline) {
|
||||||
|
return [
|
||||||
|
converter(minX, minY, minZ),
|
||||||
|
converter(maxX, minY, minZ),
|
||||||
|
converter(maxX, minY, maxZ),
|
||||||
|
converter(minX, minY, maxZ),
|
||||||
|
converter(minX, minY, minZ)
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
converter(minX, minY, minZ),
|
||||||
|
converter(maxX, minY, minZ),
|
||||||
|
converter(maxX, minY, maxZ),
|
||||||
|
converter(minX, minY, maxZ)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const get3DShapePoints = (options: DynmapArea, converter: Function): LatLngExpression[][] => {
|
||||||
|
const toplist = [],
|
||||||
|
botlist = [],
|
||||||
|
polylist = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < options.x.length; i++) {
|
||||||
|
toplist[i] = converter(options.x[i], options.y[0], options.z[i]);
|
||||||
|
botlist[i] = converter(options.x[i], options.y[1], options.z[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < options.x.length; i++) {
|
||||||
|
const sidelist = [];
|
||||||
|
sidelist[0] = toplist[i];
|
||||||
|
sidelist[1] = botlist[i];
|
||||||
|
sidelist[2] = botlist[(i + 1) % options.x.length];
|
||||||
|
sidelist[3] = toplist[(i + 1) % options.x.length];
|
||||||
|
polylist[i] = sidelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
polylist[options.x.length] = botlist;
|
||||||
|
polylist[options.x.length + 1] = toplist;
|
||||||
|
|
||||||
|
return polylist;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const get2DShapePoints = (options: DynmapArea, converter: Function, outline: boolean): LatLngExpression[] => {
|
||||||
|
const points = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < options.x.length; i++) {
|
||||||
|
points[i] = converter(options.x[i], options.y[1], options.z[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outline) {
|
||||||
|
points.push(points[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
71
src/util/circles.ts
Normal file
71
src/util/circles.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import {DynmapCircle} from "@/dynmap";
|
||||||
|
import {Polyline, Polygon, LatLngExpression} from "leaflet";
|
||||||
|
|
||||||
|
export const createCircle = (options: DynmapCircle, converter: Function): Polyline | Polygon => {
|
||||||
|
const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0),
|
||||||
|
points = getCirclePoints(options, converter, outline);
|
||||||
|
let circle;
|
||||||
|
|
||||||
|
if(outline) {
|
||||||
|
circle = new Polyline(points, options.style);
|
||||||
|
} else {
|
||||||
|
circle = new Polygon(points, options.style);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(options.label) {
|
||||||
|
circle.bindPopup(() => createPopup(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
return circle;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateCircle = (circle: Polyline | Polygon | undefined, options: DynmapCircle, converter: Function): Polyline | Polygon => {
|
||||||
|
const outline = (options.style && options.style.fillOpacity && (options.style.fillOpacity <= 0)) as boolean,
|
||||||
|
points = getCirclePoints(options, converter, outline);
|
||||||
|
|
||||||
|
if (!circle) {
|
||||||
|
return createCircle(options, converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
circle.unbindPopup();
|
||||||
|
circle.bindPopup(() => createPopup(options));
|
||||||
|
circle.setStyle(options.style);
|
||||||
|
circle.setLatLngs(points);
|
||||||
|
circle.redraw();
|
||||||
|
|
||||||
|
return circle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createPopup = (options: DynmapCircle) => {
|
||||||
|
const popup = document.createElement('span');
|
||||||
|
|
||||||
|
if (options.popupContent) {
|
||||||
|
popup.classList.add('CirclePopup');
|
||||||
|
popup.insertAdjacentHTML('afterbegin', options.popupContent);
|
||||||
|
} else if (options.isHTML) {
|
||||||
|
popup.classList.add('CirclePopup');
|
||||||
|
popup.insertAdjacentHTML('afterbegin', options.label);
|
||||||
|
} else {
|
||||||
|
popup.textContent = options.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
return popup;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCirclePoints = (options: DynmapCircle, converter: Function, outline: boolean): LatLngExpression[] => {
|
||||||
|
const points = [];
|
||||||
|
|
||||||
|
for(let i = 0; i < 360; i++) {
|
||||||
|
const rad = i * Math.PI / 180.0,
|
||||||
|
x = options.radius[0] * Math.sin(rad) + options.location.x,
|
||||||
|
z = options.radius[1] * Math.cos(rad) + options.location.z;
|
||||||
|
|
||||||
|
points.push(converter(x, options.location.y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(outline && points.length) {
|
||||||
|
points.push(points[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
};
|
55
src/util/lines.ts
Normal file
55
src/util/lines.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import {DynmapLine} from "@/dynmap";
|
||||||
|
import {Polyline, Polygon, LatLngExpression} from "leaflet";
|
||||||
|
|
||||||
|
export const createLine = (options: DynmapLine, converter: Function): Polyline | Polygon => {
|
||||||
|
const points = getLinePoints(options, converter),
|
||||||
|
line = new Polyline(points, options.style);
|
||||||
|
|
||||||
|
if(options.label) {
|
||||||
|
line.bindPopup(() => createPopup(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
return line;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateLine = (line: Polyline | Polygon | undefined, options: DynmapLine, converter: Function): Polyline | Polygon => {
|
||||||
|
const points = getLinePoints(options, converter);
|
||||||
|
|
||||||
|
if (!line) {
|
||||||
|
return createLine(options, converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
line.unbindPopup();
|
||||||
|
line.bindPopup(() => createPopup(options));
|
||||||
|
line.setStyle(options.style);
|
||||||
|
line.setLatLngs(points);
|
||||||
|
line.redraw();
|
||||||
|
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createPopup = (options: DynmapLine) => {
|
||||||
|
const popup = document.createElement('span');
|
||||||
|
|
||||||
|
if (options.popupContent) {
|
||||||
|
popup.classList.add('LinePopup');
|
||||||
|
popup.insertAdjacentHTML('afterbegin', options.popupContent);
|
||||||
|
} else if (options.isHTML) {
|
||||||
|
popup.classList.add('LinePopup');
|
||||||
|
popup.insertAdjacentHTML('afterbegin', options.label);
|
||||||
|
} else {
|
||||||
|
popup.textContent = options.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
return popup;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getLinePoints = (options: DynmapLine, converter: Function): LatLngExpression[] => {
|
||||||
|
const points = [];
|
||||||
|
|
||||||
|
for(let i = 0; i < options.x.length; i++) {
|
||||||
|
points.push(converter(options.x[i], options.y[i], options.z[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user