Overhaul marker update handling

- Renamed pendingSetUpdates to pendingMarkerUpdates
- pendingMarkerUpdates is now a simple array of LiveAtlasMarkerUpdates
- Added separate ADD_MARKER_UPDATES mutation for marker updates
- Set updates via ADD_MARKER_SET_UPDATES are applied immediately and not stored in state
- Removed all marker pop actions and mutations except POP_MARKER_UPDATES, which now returns all marker types
- Added centralised markup update handler in markers.ts, replacing much of the logic in the individual marker type components. Allows additional handlers to be registered for specific marker sets and types
This commit is contained in:
James Lyne 2022-01-15 00:07:25 +00:00
parent 61b6b820aa
commit 9abd96ccb1
13 changed files with 392 additions and 549 deletions

View File

@ -15,15 +15,16 @@
-->
<script lang="ts">
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
import {defineComponent, computed, onMounted, watch, onUnmounted} from "@vue/runtime-core";
import {useStore} from "@/store";
import {ActionTypes} from "@/store/action-types";
import {createArea, updateArea} from "@/util/areas";
import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup";
import LiveAtlasPolygon from "@/leaflet/vector/LiveAtlasPolygon";
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
import {LiveAtlasAreaMarker, LiveAtlasMarkerSet} from "@/index";
import {nonReactiveState} from "@/store/state";
import {DynmapMarkerUpdate} from "@/dynmap";
import {LiveAtlasMarkerType, registerTypeUpdateHandler, unregisterTypeUpdateHandler} from "@/util/markers";
export default defineComponent({
props: {
@ -38,71 +39,49 @@ export default defineComponent({
},
setup(props) {
let updateFrame = 0;
const store = useStore(),
currentMap = computed(() => store.state.currentMap),
pendingUpdates = computed(() => {
const markerSetUpdates = store.state.pendingSetUpdates.get(props.set.id);
layers = Object.freeze(new Map()) as Map<string, LiveAtlasPolygon | LiveAtlasPolyline>;
return markerSetUpdates && markerSetUpdates.areaUpdates.length;
}),
layers = Object.freeze(new Map()) as Map<string, LiveAtlasPolygon | LiveAtlasPolyline>,
let converter = currentMap.value!.locationToLatLng.bind(currentMap.value);
createAreas = () => {
const converter = currentMap.value!.locationToLatLng.bind(currentMap.value);
const createAreas = () => {
nonReactiveState.markers.get(props.set.id)!.areas.forEach((area: LiveAtlasAreaMarker, id: string) => {
const layer = createArea(area, converter);
nonReactiveState.markers.get(props.set.id)!.areas.forEach((area: LiveAtlasAreaMarker, id: string) => {
const layer = createArea(area, converter);
layers.set(id, layer);
props.layerGroup.addLayer(layer);
});
};
layers.set(id, layer);
const deleteArea = (id: string) => {
let area = layers.get(id) as LiveAtlasPolyline;
if(!area) {
return;
}
props.layerGroup.removeLayer(area);
layers.delete(id);
};
const handleUpdate = (update: DynmapMarkerUpdate) => {
if(update.removed) {
deleteArea(update.id);
} else {
const layer = updateArea(layers.get(update.id), update.payload as LiveAtlasAreaMarker, converter);
if(!layers.has(update.id)) {
props.layerGroup.addLayer(layer);
});
},
deleteArea = (id: string) => {
let area = layers.get(id) as LiveAtlasPolyline;
if(!area) {
return;
}
props.layerGroup.removeLayer(area);
layers.delete(id);
},
handlePendingUpdates = async () => {
const updates = await store.dispatch(ActionTypes.POP_AREA_UPDATES, {
markerSet: props.set.id,
amount: 10,
}),
converter = currentMap.value!.locationToLatLng.bind(currentMap.value);
for(const update of updates) {
if(update.removed) {
deleteArea(update.id);
} else {
const layer = updateArea(layers.get(update.id), update.payload as LiveAtlasAreaMarker, converter);
if(!layers.has(update.id)) {
props.layerGroup.addLayer(layer);
}
layers.set(update.id, layer);
}
}
if(pendingUpdates.value) {
// eslint-disable-next-line no-unused-vars
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
} else {
updateFrame = 0;
}
};
layers.set(update.id, layer);
}
};
watch(currentMap, (newValue, oldValue) => {
if(newValue && (!oldValue || oldValue.world === newValue.world)) {
const converter = newValue.locationToLatLng.bind(newValue);
converter = newValue.locationToLatLng.bind(newValue);
for (const [id, area] of nonReactiveState.markers.get(props.set.id)!.areas) {
updateArea(layers.get(id), area, converter);
@ -110,14 +89,13 @@ export default defineComponent({
}
});
watch(pendingUpdates, (newValue, oldValue) => {
if(newValue && newValue > 0 && oldValue === 0 && !updateFrame) {
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
}
onMounted(() => {
createAreas();
registerTypeUpdateHandler(handleUpdate, props.set.id, LiveAtlasMarkerType.AREA);
});
onUnmounted(() => {
unregisterTypeUpdateHandler(handleUpdate, props.set.id, LiveAtlasMarkerType.AREA);
});
onMounted(() => createAreas());
onUnmounted(() => updateFrame && cancelAnimationFrame(updateFrame));
},
render() {

View File

@ -15,15 +15,16 @@
-->
<script lang="ts">
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
import {defineComponent, computed, onMounted, watch, onUnmounted} from "@vue/runtime-core";
import {useStore} from "@/store";
import {ActionTypes} from "@/store/action-types";
import {createCircle, updateCircle} from "@/util/circles";
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
import LiveAtlasPolygon from "@/leaflet/vector/LiveAtlasPolygon";
import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup";
import {LiveAtlasCircleMarker, LiveAtlasMarkerSet} from "@/index";
import {nonReactiveState} from "@/store/state";
import {DynmapMarkerUpdate} from "@/dynmap";
import {LiveAtlasMarkerType, registerTypeUpdateHandler, unregisterTypeUpdateHandler} from "@/util/markers";
export default defineComponent({
props: {
@ -38,71 +39,49 @@ export default defineComponent({
},
setup(props) {
let updateFrame = 0;
const store = useStore(),
currentMap = computed(() => store.state.currentMap),
pendingUpdates = computed(() => {
const markerSetUpdates = store.state.pendingSetUpdates.get(props.set.id);
layers = Object.freeze(new Map<string, LiveAtlasPolyline | LiveAtlasPolygon>());
return markerSetUpdates && markerSetUpdates.circleUpdates.length;
}),
layers = Object.freeze(new Map<string, LiveAtlasPolyline | LiveAtlasPolygon>()),
let converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
createCircles = () => {
const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
const createCircles = () => {
nonReactiveState.markers.get(props.set.id)!.circles.forEach((circle: LiveAtlasCircleMarker, id: string) => {
const layer = createCircle(circle, converter);
nonReactiveState.markers.get(props.set.id)!.circles.forEach((circle: LiveAtlasCircleMarker, id: string) => {
const layer = createCircle(circle, converter);
layers.set(id, layer);
props.layerGroup.addLayer(layer);
});
};
layers.set(id, layer);
const deleteCircle = (id: string) => {
let circle = layers.get(id) as LiveAtlasPolyline;
if (!circle) {
return;
}
props.layerGroup.removeLayer(circle);
layers.delete(id);
};
const handleUpdate = (update: DynmapMarkerUpdate) => {
if(update.removed) {
deleteCircle(update.id);
} else {
const layer = updateCircle(layers.get(update.id), update.payload as LiveAtlasCircleMarker, converter);
if(!layers.has(update.id)) {
props.layerGroup.addLayer(layer);
});
},
deleteCircle = (id: string) => {
let circle = layers.get(id) as LiveAtlasPolyline;
if (!circle) {
return;
}
props.layerGroup.removeLayer(circle);
layers.delete(id);
},
handlePendingUpdates = async () => {
const updates = await store.dispatch(ActionTypes.POP_CIRCLE_UPDATES, {
markerSet: props.set.id,
amount: 10,
}),
converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
for(const update of updates) {
if(update.removed) {
deleteCircle(update.id);
} else {
const layer = updateCircle(layers.get(update.id), update.payload as LiveAtlasCircleMarker, converter)
if(!layers.has(update.id)) {
props.layerGroup.addLayer(layer);
}
layers.set(update.id, layer);
}
}
if(pendingUpdates.value) {
// eslint-disable-next-line no-unused-vars
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
} else {
updateFrame = 0;
}
};
layers.set(update.id, layer);
}
};
watch(currentMap, (newValue, oldValue) => {
if(newValue && (!oldValue || oldValue.world === newValue.world)) {
const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
for (const [id, circle] of nonReactiveState.markers.get(props.set.id)!.circles) {
updateCircle(layers.get(id), circle, converter);
@ -110,14 +89,13 @@ export default defineComponent({
}
});
watch(pendingUpdates, (newValue, oldValue) => {
if(newValue && newValue > 0 && oldValue === 0 && !updateFrame) {
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
}
onMounted(() => {
createCircles();
registerTypeUpdateHandler(handleUpdate, props.set.id, LiveAtlasMarkerType.CIRCLE);
});
onUnmounted(() => {
unregisterTypeUpdateHandler(handleUpdate, props.set.id, LiveAtlasMarkerType.CIRCLE);
});
onMounted(() => createCircles());
onUnmounted(() => updateFrame && cancelAnimationFrame(updateFrame));
},
render() {

View File

@ -15,14 +15,15 @@
-->
<script lang="ts">
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
import {defineComponent, computed, onMounted, watch, onUnmounted} from "@vue/runtime-core";
import {useStore} from "@/store";
import {ActionTypes} from "@/store/action-types";
import {createLine, updateLine} from "@/util/lines";
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup";
import {LiveAtlasLineMarker, LiveAtlasMarkerSet} from "@/index";
import {nonReactiveState} from "@/store/state";
import {DynmapMarkerUpdate} from "@/dynmap";
import {LiveAtlasMarkerType, registerTypeUpdateHandler, unregisterTypeUpdateHandler} from "@/util/markers";
export default defineComponent({
props: {
@ -37,71 +38,49 @@ export default defineComponent({
},
setup(props) {
let updateFrame = 0;
const store = useStore(),
currentMap = computed(() => store.state.currentMap),
pendingUpdates = computed(() => {
const markerSetUpdates = store.state.pendingSetUpdates.get(props.set.id);
layers = Object.freeze(new Map<string, LiveAtlasPolyline>());
return markerSetUpdates && markerSetUpdates.lineUpdates.length;
}),
layers = Object.freeze(new Map<string, LiveAtlasPolyline>()),
let converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap)
createLines = () => {
const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
const createLines = () => {
nonReactiveState.markers.get(props.set.id)!.lines.forEach((line: LiveAtlasLineMarker, id: string) => {
const layer = createLine(line, converter);
nonReactiveState.markers.get(props.set.id)!.lines.forEach((line: LiveAtlasLineMarker, id: string) => {
const layer = createLine(line, converter);
layers.set(id, layer);
props.layerGroup.addLayer(layer);
});
};
layers.set(id, layer);
const deleteLine = (id: string) => {
let line = layers.get(id) as LiveAtlasPolyline;
if (!line) {
return;
}
props.layerGroup.removeLayer(line);
layers.delete(id);
};
const handleUpdate = (update: DynmapMarkerUpdate) => {
if(update.removed) {
deleteLine(update.id);
} else {
const layer = updateLine(layers.get(update.id), update.payload as LiveAtlasLineMarker, converter);
if(!layers.has(update.id)) {
props.layerGroup.addLayer(layer);
});
},
deleteLine = (id: string) => {
let line = layers.get(id) as LiveAtlasPolyline;
if (!line) {
return;
}
props.layerGroup.removeLayer(line);
layers.delete(id);
},
handlePendingUpdates = async () => {
const updates = await store.dispatch(ActionTypes.POP_LINE_UPDATES, {
markerSet: props.set.id,
amount: 10,
}),
converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
for(const update of updates) {
if(update.removed) {
deleteLine(update.id);
} else {
const layer = updateLine(layers.get(update.id), update.payload as LiveAtlasLineMarker, converter)
if(!layers.has(update.id)) {
props.layerGroup.addLayer(layer);
}
layers.set(update.id, layer);
}
}
if(pendingUpdates.value) {
// eslint-disable-next-line no-unused-vars
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
} else {
updateFrame = 0;
}
};
layers.set(update.id, layer);
}
};
watch(currentMap, (newValue, oldValue) => {
if(newValue && (!oldValue || oldValue.world === newValue.world)) {
const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
for (const [id, line] of nonReactiveState.markers.get(props.set.id)!.lines) {
updateLine(layers.get(id), line, converter);
@ -109,14 +88,13 @@ export default defineComponent({
}
});
watch(pendingUpdates, (newValue, oldValue) => {
if(newValue && newValue > 0 && oldValue === 0 && !updateFrame) {
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
}
onMounted(() => {
createLines();
registerTypeUpdateHandler(handleUpdate, props.set.id, LiveAtlasMarkerType.LINE);
});
onUnmounted(() => {
unregisterTypeUpdateHandler(handleUpdate, props.set.id, LiveAtlasMarkerType.LINE);
});
onMounted(() => createLines());
onUnmounted(() => updateFrame && cancelAnimationFrame(updateFrame));
},
render() {

View File

@ -15,14 +15,15 @@
-->
<script lang="ts">
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
import {defineComponent, computed, onMounted, watch, onUnmounted} from "@vue/runtime-core";
import {Marker} from 'leaflet';
import {useStore} from "@/store";
import {ActionTypes} from "@/store/action-types";
import {createPointMarker, updatePointMarker} from "@/util/points";
import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup";
import {LiveAtlasPointMarker, LiveAtlasMarkerSet} from "@/index";
import {nonReactiveState} from "@/store/state";
import {DynmapMarkerUpdate} from "@/dynmap";
import {LiveAtlasMarkerType, registerTypeUpdateHandler, unregisterTypeUpdateHandler} from "@/util/markers";
export default defineComponent({
props: {
@ -37,71 +38,49 @@ export default defineComponent({
},
setup(props) {
let updateFrame = 0;
const store = useStore(),
currentMap = computed(() => store.state.currentMap),
pendingUpdates = computed(() => {
const markerSetUpdates = store.state.pendingSetUpdates.get(props.set.id);
layers = Object.freeze(new Map()) as Map<string, Marker>;
return markerSetUpdates && markerSetUpdates.markerUpdates.length;
}),
layers = Object.freeze(new Map()) as Map<string, Marker>,
let converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
createMarkers = () => {
const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
const createMarkers = () => {
nonReactiveState.markers.get(props.set.id)!.points.forEach((marker: LiveAtlasPointMarker, id: string) => {
const layer = createPointMarker(marker, converter);
nonReactiveState.markers.get(props.set.id)!.points.forEach((marker: LiveAtlasPointMarker, id: string) => {
const layer = createPointMarker(marker, converter);
layers.set(id, layer);
props.layerGroup.addLayer(layer);
});
};
layers.set(id, layer);
const deleteMarker = (id: string) => {
let marker = layers.get(id) as Marker;
if(!marker) {
return;
}
props.layerGroup.removeLayer(marker);
layers.delete(id);
};
const handleUpdate = (update: DynmapMarkerUpdate) => {
if(update.removed) {
deleteMarker(update.id);
} else {
const layer = updatePointMarker(layers.get(update.id), update.payload as LiveAtlasPointMarker, converter);
if(!layers.has(update.id)) {
props.layerGroup.addLayer(layer);
});
},
deleteMarker = (id: string) => {
let marker = layers.get(id) as Marker;
if(!marker) {
return;
}
props.layerGroup.removeLayer(marker);
layers.delete(id);
},
handlePendingUpdates = async () => {
const updates = await store.dispatch(ActionTypes.POP_MARKER_UPDATES, {
markerSet: props.set.id,
amount: 10,
}),
converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
for(const update of updates) {
if(update.removed) {
deleteMarker(update.id);
} else {
const layer = updatePointMarker(layers.get(update.id), update.payload as LiveAtlasPointMarker, converter);
if(!layers.has(update.id)) {
props.layerGroup.addLayer(layer);
}
layers.set(update.id, layer);
}
}
if(pendingUpdates.value) {
// eslint-disable-next-line no-unused-vars
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
} else {
updateFrame = 0;
}
};
layers.set(update.id, layer);
}
};
watch(currentMap, (newValue, oldValue) => {
if(newValue && (!oldValue || oldValue.world === newValue.world)) {
const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
for (const [id, marker] of nonReactiveState.markers.get(props.set.id)!.points) {
updatePointMarker(layers.get(id), marker, converter);
@ -109,14 +88,13 @@ export default defineComponent({
}
});
watch(pendingUpdates, (newValue, oldValue) => {
if(newValue && newValue > 0 && oldValue === 0 && !updateFrame) {
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
}
onMounted(() => {
createMarkers();
registerTypeUpdateHandler(handleUpdate, props.set.id, LiveAtlasMarkerType.POINT);
});
onUnmounted(() => {
unregisterTypeUpdateHandler(handleUpdate, props.set.id, LiveAtlasMarkerType.POINT);
});
onMounted(() => createMarkers());
onUnmounted(() => updateFrame && cancelAnimationFrame(updateFrame));
},
render() {

31
src/dynmap.d.ts vendored
View File

@ -14,7 +14,13 @@
* limitations under the License.
*/
import {LiveAtlasAreaMarker, LiveAtlasCircleMarker, LiveAtlasLineMarker, LiveAtlasPointMarker} from "@/index";
import {
LiveAtlasAreaMarker,
LiveAtlasCircleMarker,
LiveAtlasLineMarker,
LiveAtlasMarker, LiveAtlasMarkerType,
LiveAtlasPointMarker
} from "@/index";
declare global {
// noinspection JSUnusedGlobalSymbols
@ -33,12 +39,9 @@ type DynmapUrlConfig = {
markers: string;
}
interface DynmapMarkerSetUpdates {
markerUpdates: Array<DynmapMarkerUpdate>
areaUpdates: Array<DynmapAreaUpdate>
circleUpdates: Array<DynmapCircleUpdate>
lineUpdates: Array<DynmapLineUpdate>
removed?: boolean
interface DynmapMarkerSetUpdate {
id: string,
removed: boolean
payload?: {
showLabels: boolean;
hidden: boolean;
@ -49,25 +52,27 @@ interface DynmapMarkerSetUpdates {
}
}
interface DynmapUpdate {
interface DynmapMarkerUpdate {
set: string,
id: string,
type: LiveAtlasMarkerType,
removed: boolean,
payload?: any,
payload: LiveAtlasMarker,
}
interface DynmapMarkerUpdate extends DynmapUpdate {
interface DynmapPointUpdate extends DynmapMarkerUpdate {
payload?: LiveAtlasPointMarker
}
interface DynmapAreaUpdate extends DynmapUpdate {
interface DynmapAreaUpdate extends DynmapMarkerUpdate {
payload?: LiveAtlasAreaMarker
}
interface DynmapCircleUpdate extends DynmapUpdate {
interface DynmapCircleUpdate extends DynmapMarkerUpdate {
payload?: LiveAtlasCircleMarker
}
interface DynmapLineUpdate extends DynmapUpdate {
interface DynmapLineUpdate extends DynmapMarkerUpdate {
payload?: LiveAtlasLineMarker
}

View File

@ -183,6 +183,7 @@ export default class DynmapMapProvider extends MapProvider {
this.store.commit(MutationTypes.SET_WORLD_STATE, worldState);
this.store.commit(MutationTypes.ADD_MARKER_SET_UPDATES, updates.markerSets);
this.store.commit(MutationTypes.ADD_MARKER_UPDATES, updates.markers);
this.store.commit(MutationTypes.ADD_TILE_UPDATES, updates.tiles);
this.store.commit(MutationTypes.ADD_CHAT, updates.chat);

View File

@ -20,9 +20,6 @@ export enum ActionTypes {
STOP_UPDATES = "stopUpdates",
SET_PLAYERS = "setPlayers",
POP_MARKER_UPDATES = "popMarkerUpdates",
POP_AREA_UPDATES = "popAreaUpdates",
POP_CIRCLE_UPDATES = "popCircleUpdates",
POP_LINE_UPDATES = "popLineUpdates",
POP_TILE_UPDATES = "popTileUpdates",
SEND_CHAT_MESSAGE = "sendChatMessage",
LOGIN = "login",

View File

@ -19,13 +19,10 @@ import {ActionContext, ActionTree} from "vuex";
import {State} from "@/store/state";
import {ActionTypes} from "@/store/action-types";
import {Mutations} from "@/store/mutations";
import {
DynmapAreaUpdate, DynmapCircleUpdate, DynmapLineUpdate,
DynmapMarkerUpdate,
DynmapTileUpdate,
} from "@/dynmap";
import {DynmapMarkerUpdate, DynmapTileUpdate} from "@/dynmap";
import {LiveAtlasMarkerSet, LiveAtlasPlayer, LiveAtlasWorldDefinition} from "@/index";
import {nextTick} from "vue";
import {startUpdateHandling, stopUpdateHandling} from "@/util/markers";
type AugmentedActionContext = {
commit<K extends keyof Mutations>(
@ -50,20 +47,8 @@ export interface Actions {
):Promise<Map<string, LiveAtlasMarkerSet>>
[ActionTypes.POP_MARKER_UPDATES](
{commit}: AugmentedActionContext,
payload: {markerSet: string, amount: number}
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[]>
[ActionTypes.POP_TILE_UPDATES](
{commit}: AugmentedActionContext,
payload: number
@ -159,10 +144,12 @@ export const actions: ActionTree<State, State> & Actions = {
}
state.currentMapProvider!.startUpdates();
startUpdateHandling();
},
async [ActionTypes.STOP_UPDATES]({state}) {
state.currentMapProvider!.stopUpdates();
stopUpdateHandling();
},
[ActionTypes.SET_PLAYERS]({commit, state}, players: Set<LiveAtlasPlayer>) {
@ -191,54 +178,10 @@ export const actions: ActionTree<State, State> & Actions = {
});
},
async [ActionTypes.POP_MARKER_UPDATES]({commit, state}, {markerSet, amount}: {markerSet: string, amount: number}): Promise<DynmapMarkerUpdate[]> {
if(!state.markerSets.has(markerSet)) {
console.warn(`POP_MARKER_UPDATES: Marker set ${markerSet} doesn't exist`);
return [];
}
async [ActionTypes.POP_MARKER_UPDATES]({commit, state}, amount: number): Promise<DynmapMarkerUpdate[]> {
const updates = state.pendingMarkerUpdates.slice(0, amount);
const updates = state.pendingSetUpdates.get(markerSet)!.markerUpdates.slice(0, amount);
commit(MutationTypes.POP_MARKER_UPDATES, {markerSet, amount});
return updates;
},
async [ActionTypes.POP_AREA_UPDATES]({commit, state}, {markerSet, amount}: {markerSet: string, amount: number}): Promise<DynmapAreaUpdate[]> {
if(!state.markerSets.has(markerSet)) {
console.warn(`POP_AREA_UPDATES: Marker set ${markerSet} doesn't exist`);
return [];
}
const updates = state.pendingSetUpdates.get(markerSet)!.areaUpdates.slice(0, amount);
commit(MutationTypes.POP_AREA_UPDATES, {markerSet, amount});
return updates;
},
async [ActionTypes.POP_CIRCLE_UPDATES]({commit, state}, {markerSet, amount}: {markerSet: string, amount: number}): Promise<DynmapCircleUpdate[]> {
if(!state.markerSets.has(markerSet)) {
console.warn(`POP_CIRCLE_UPDATES: Marker set ${markerSet} doesn't exist`);
return [];
}
const updates = state.pendingSetUpdates.get(markerSet)!.circleUpdates.slice(0, amount);
commit(MutationTypes.POP_CIRCLE_UPDATES, {markerSet, amount});
return updates;
},
async [ActionTypes.POP_LINE_UPDATES]({commit, state}, {markerSet, amount}: {markerSet: string, amount: number}): Promise<DynmapLineUpdate[]> {
if(!state.markerSets.has(markerSet)) {
console.warn(`POP_LINE_UPDATES: Marker set ${markerSet} doesn't exist`);
return [];
}
const updates = state.pendingSetUpdates.get(markerSet)!.lineUpdates.slice(0, amount);
commit(MutationTypes.POP_LINE_UPDATES, {markerSet, amount});
commit(MutationTypes.POP_MARKER_UPDATES, amount);
return updates;
},

View File

@ -26,12 +26,10 @@ export enum MutationTypes {
SET_MARKERS = 'setMarkers',
SET_WORLD_STATE = 'setWorldState',
ADD_MARKER_SET_UPDATES = 'addMarkerSetUpdates',
ADD_MARKER_UPDATES = 'addMarkerUpdates',
ADD_TILE_UPDATES = 'addTileUpdates',
ADD_CHAT = 'addChat',
POP_MARKER_UPDATES = 'popMarkerUpdates',
POP_AREA_UPDATES = 'popAreaUpdates',
POP_CIRCLE_UPDATES = 'popCircleUpdates',
POP_LINE_UPDATES = 'popLineUpdates',
POP_TILE_UPDATES = 'popTileUpdates',
SET_MAX_PLAYERS = 'setMaxPlayers',
SET_PLAYERS_ASYNC = 'setPlayersAsync',

View File

@ -18,7 +18,7 @@ import {MutationTree} from "vuex";
import {MutationTypes} from "@/store/mutation-types";
import {nonReactiveState, State} from "@/store/state";
import {
DynmapMarkerSetUpdates,
DynmapMarkerSetUpdate, DynmapMarkerUpdate,
DynmapTileUpdate
} from "@/dynmap";
import {
@ -48,6 +48,7 @@ import {
import DynmapMapProvider from "@/providers/DynmapMapProvider";
import Pl3xmapMapProvider from "@/providers/Pl3xmapMapProvider";
import {getGlobalMessages} from "@/util";
import {LiveAtlasMarkerType} from "@/util/markers";
export type CurrentMapPayload = {
worldName: string;
@ -64,14 +65,12 @@ export type Mutations<S = State> = {
[MutationTypes.SET_MARKER_SETS](state: S, markerSets: Map<string, LiveAtlasMarkerSet>): void
[MutationTypes.SET_MARKERS](state: S, markers: Map<string, LiveAtlasMarkerSetContents>): void
[MutationTypes.SET_WORLD_STATE](state: S, worldState: LiveAtlasWorldState): void
[MutationTypes.ADD_MARKER_SET_UPDATES](state: S, updates: Map<string, DynmapMarkerSetUpdates>): void
[MutationTypes.ADD_MARKER_SET_UPDATES](state: S, updates: DynmapMarkerSetUpdate[]): void
[MutationTypes.ADD_MARKER_UPDATES](state: S, updates: DynmapMarkerUpdate[]): void
[MutationTypes.ADD_TILE_UPDATES](state: S, updates: Array<DynmapTileUpdate>): void
[MutationTypes.ADD_CHAT](state: State, chat: Array<LiveAtlasChat>): void
[MutationTypes.POP_MARKER_UPDATES](state: S, payload: {markerSet: string, amount: number}): void
[MutationTypes.POP_AREA_UPDATES](state: S, payload: {markerSet: string, amount: number}): void
[MutationTypes.POP_CIRCLE_UPDATES](state: S, payload: {markerSet: string, amount: number}): void
[MutationTypes.POP_LINE_UPDATES](state: S, payload: {markerSet: string, amount: number}): void
[MutationTypes.POP_MARKER_UPDATES](state: S, amount: number): void
[MutationTypes.POP_TILE_UPDATES](state: S, amount: number): void
[MutationTypes.SET_MAX_PLAYERS](state: S, maxPlayers: number): void
@ -205,17 +204,11 @@ export const mutations: MutationTree<State> & Mutations = {
//Sets the existing marker sets from the last marker fetch
[MutationTypes.SET_MARKER_SETS](state: State, markerSets: Map<string, LiveAtlasMarkerSet>) {
state.markerSets.clear();
state.pendingSetUpdates.clear();
state.pendingMarkerUpdates.splice(0);
nonReactiveState.markers.clear();
for(const entry of markerSets) {
state.markerSets.set(entry[0], entry[1]);
state.pendingSetUpdates.set(entry[0], {
markerUpdates: [],
areaUpdates: [],
circleUpdates: [],
lineUpdates: [],
});
nonReactiveState.markers.set(entry[0], {
points: new Map<string, LiveAtlasPointMarker>(),
areas: new Map<string, LiveAtlasAreaMarker>(),
@ -240,96 +233,95 @@ export const mutations: MutationTree<State> & Mutations = {
},
//Adds markerset related updates from an update fetch to the pending updates list
[MutationTypes.ADD_MARKER_SET_UPDATES](state: State, updates: Map<string, DynmapMarkerSetUpdates>) {
for(const entry of updates) {
if(!state.markerSets.has(entry[0])) {
//Create marker set if it doesn't exist
if(entry[1].payload) {
state.markerSets.set(entry[0], {
id: entry[0],
showLabels: entry[1].payload.showLabels,
minZoom: entry[1].payload.minZoom,
maxZoom: entry[1].payload.maxZoom,
priority: entry[1].payload.priority,
label: entry[1].payload.label,
hidden: entry[1].payload.hidden,
});
state.pendingSetUpdates.set(entry[0], {
markerUpdates: [],
areaUpdates: [],
circleUpdates: [],
lineUpdates: [],
});
} else {
console.warn(`ADD_MARKER_SET_UPDATES: Marker set ${entry[0]} doesn't exist`);
continue;
}
}
const set = state.markerSets.get(entry[0]) as LiveAtlasMarkerSet,
setContents = nonReactiveState.markers.get(entry[0]) as LiveAtlasMarkerSetContents,
setUpdates = state.pendingSetUpdates.get(entry[0]) as DynmapMarkerSetUpdates;
//Delete the set if it has been deleted
if(entry[1].removed) {
state.markerSets.delete(entry[0]);
state.pendingSetUpdates.delete(entry[0]);
[MutationTypes.ADD_MARKER_SET_UPDATES](state: State, updates: DynmapMarkerSetUpdate[]) {
for(const update of updates) {
if(update.removed) {
state.markerSets.delete(update.id);
nonReactiveState.markers.delete(update.id);
continue;
}
//Update the set itself if a payload exists
if(entry[1].payload) {
set.showLabels = entry[1].payload.showLabels;
set.minZoom = entry[1].payload.minZoom;
set.maxZoom = entry[1].payload.maxZoom;
set.priority = entry[1].payload.priority;
set.label = entry[1].payload.label;
set.hidden = entry[1].payload.hidden;
}
if(update.payload) {
if(state.markerSets.has(update.id)) { //Update if exists
const set = state.markerSets.get(update.id) as LiveAtlasMarkerSet;
//Update non-reactive lists
for(const update of entry[1].markerUpdates) {
if(update.removed) {
setContents.points.delete(update.id);
} else {
setContents.points.set(update.id, update.payload as LiveAtlasPointMarker);
set.showLabels = update.payload.showLabels;
set.minZoom = update.payload.minZoom;
set.maxZoom = update.payload.maxZoom;
set.priority = update.payload.priority;
set.label = update.payload.label;
set.hidden = update.payload.hidden;
} else { //Otherwise create
state.markerSets.set(update.id, {
id: update.id,
showLabels: update.payload.showLabels,
minZoom: update.payload.minZoom,
maxZoom: update.payload.maxZoom,
priority: update.payload.priority,
label: update.payload.label,
hidden: update.payload.hidden,
});
nonReactiveState.markers.set(update.id, {
points: new Map<string, LiveAtlasPointMarker>(),
areas: new Map<string, LiveAtlasAreaMarker>(),
lines: new Map<string, LiveAtlasLineMarker>(),
circles: new Map<string, LiveAtlasCircleMarker>(),
});
}
}
for(const update of entry[1].areaUpdates) {
if(update.removed) {
setContents.areas.delete(update.id);
} else {
setContents.areas.set(update.id, update.payload as LiveAtlasAreaMarker);
}
}
for(const update of entry[1].circleUpdates) {
if(update.removed) {
setContents.circles.delete(update.id);
} else {
setContents.circles.set(update.id, update.payload as LiveAtlasCircleMarker);
}
}
for(const update of entry[1].lineUpdates) {
if(update.removed) {
setContents.lines.delete(update.id);
} else {
setContents.lines.set(update.id, update.payload as LiveAtlasLineMarker);
}
}
//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);
}
},
//Sets the existing marker sets from the last marker fetch
[MutationTypes.ADD_MARKER_UPDATES](state: State, markers: DynmapMarkerUpdate[]) {
let setContents;
for (const update of markers) {
if(!nonReactiveState.markers.has(update.set)) {
continue;
}
setContents = nonReactiveState.markers.get(update.set) as LiveAtlasMarkerSetContents;
switch (update.type) {
case LiveAtlasMarkerType.POINT:
if(update.removed) {
setContents.points.delete(update.id);
} else {
setContents.points.set(update.id, update.payload as LiveAtlasPointMarker);
}
continue;
case LiveAtlasMarkerType.AREA:
if(update.removed) {
setContents.areas.delete(update.id);
} else {
setContents.areas.set(update.id, update.payload as LiveAtlasAreaMarker);
}
continue;
case LiveAtlasMarkerType.LINE:
if(update.removed) {
setContents.lines.delete(update.id);
} else {
setContents.lines.set(update.id, update.payload as LiveAtlasLineMarker);
}
continue;
case LiveAtlasMarkerType.CIRCLE:
if(update.removed) {
setContents.circles.delete(update.id);
} else {
setContents.circles.set(update.id, update.payload as LiveAtlasCircleMarker);
}
}
}
state.pendingMarkerUpdates = state.pendingMarkerUpdates.concat(markers);
},
//Adds tile updates from an update fetch to the pending updates list
[MutationTypes.ADD_TILE_UPDATES](state: State, updates: Array<DynmapTileUpdate>) {
state.pendingTileUpdates = state.pendingTileUpdates.concat(updates);
@ -341,43 +333,8 @@ export const mutations: MutationTree<State> & Mutations = {
},
//Pops the specified number of marker updates from the pending updates list
[MutationTypes.POP_MARKER_UPDATES](state: State, {markerSet, amount}) {
if(!state.markerSets.has(markerSet)) {
console.warn(`POP_MARKER_UPDATES: Marker set ${markerSet} doesn't exist`);
return;
}
state.pendingSetUpdates.get(markerSet)!.markerUpdates.splice(0, amount);
},
//Pops the specified number of area updates from the pending updates list
[MutationTypes.POP_AREA_UPDATES](state: State, {markerSet, amount}) {
if(!state.markerSets.has(markerSet)) {
console.warn(`POP_AREA_UPDATES: Marker set ${markerSet} doesn't exist`);
return;
}
state.pendingSetUpdates.get(markerSet)!.areaUpdates.splice(0, amount);
},
//Pops the specified number of circle updates from the pending updates list
[MutationTypes.POP_CIRCLE_UPDATES](state: State, {markerSet, amount}) {
if(!state.markerSets.has(markerSet)) {
console.warn(`POP_CIRCLE_UPDATES: Marker set ${markerSet} doesn't exist`);
return;
}
state.pendingSetUpdates.get(markerSet)!.circleUpdates.splice(0, amount);
},
//Pops the specified number of line updates from the pending updates list
[MutationTypes.POP_LINE_UPDATES](state: State, {markerSet, amount}) {
if(!state.markerSets.has(markerSet)) {
console.warn(`POP_LINE_UPDATES: Marker set ${markerSet} doesn't exist`);
return;
}
state.pendingSetUpdates.get(markerSet)!.lineUpdates.splice(0, amount);
[MutationTypes.POP_MARKER_UPDATES](state: State, amount: number) {
state.pendingMarkerUpdates.splice(0, amount);
},
//Pops the specified number of tile updates from the pending updates list
@ -502,8 +459,8 @@ export const mutations: MutationTree<State> & Mutations = {
if(state.currentWorld !== newWorld) {
state.currentWorld = state.worlds.get(worldName);
state.markerSets.clear();
state.pendingSetUpdates.clear();
state.pendingTileUpdates = [];
state.pendingMarkerUpdates.splice(0);
state.pendingTileUpdates.splice(0);
}
state.currentMap = state.maps.get(mapName);
@ -629,8 +586,8 @@ export const mutations: MutationTree<State> & Mutations = {
state.currentMap = undefined;
state.markerSets.clear();
state.pendingSetUpdates.clear();
state.pendingTileUpdates = [];
state.pendingMarkerUpdates.splice(0);
state.pendingTileUpdates.splice(0);
state.worlds.clear();
state.maps.clear();

View File

@ -15,7 +15,7 @@
*/
import {
DynmapMarkerSetUpdates,
DynmapMarkerUpdate,
DynmapTileUpdate
} from "@/dynmap";
import {
@ -66,7 +66,7 @@ export type State = {
messages: LiveAtlasChat[];
};
pendingSetUpdates: Map<string, DynmapMarkerSetUpdates>;
pendingMarkerUpdates: DynmapMarkerUpdate[];
pendingTileUpdates: Array<DynmapTileUpdate>;
followTarget?: LiveAtlasPlayer;
@ -134,7 +134,7 @@ export const state: State = {
markerSets: new Map(), //Markers sets from world_markers.json, doesn't include the markers themselves for performance reasons
pendingSetUpdates: new Map(), //Pending updates to markers/areas/etc for each marker set
pendingMarkerUpdates: [], //Pending updates to markers/areas/etc for each marker set
pendingTileUpdates: [], //Pending updates to map tiles
//Dynmap optional components

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
import {DynmapMarkerSetUpdates, DynmapTileUpdate, DynmapUpdate} from "@/dynmap";
import {DynmapMarkerSetUpdate, DynmapMarkerUpdate, DynmapTileUpdate} from "@/dynmap";
import {
LiveAtlasAreaMarker,
LiveAtlasChat,
@ -46,6 +46,7 @@ import {
WorldMapConfiguration
} from "dynmap";
import {PointTuple} from "leaflet";
import {LiveAtlasMarkerType} from "@/util/markers";
export function buildServerConfig(response: Options): LiveAtlasServerConfig {
let title = 'Dynmap';
@ -441,7 +442,8 @@ export function buildCircle(circle: MarkerCircle): LiveAtlasCircleMarker {
export function buildUpdates(data: Array<any>, lastUpdate: Date) {
const updates = {
markerSets: new Map<string, DynmapMarkerSetUpdates>(),
markerSets: [] as DynmapMarkerSetUpdate[],
markers: [] as DynmapMarkerUpdate[],
tiles: [] as DynmapTileUpdate[],
chat: [] as LiveAtlasChat[],
},
@ -483,39 +485,34 @@ export function buildUpdates(data: Array<any>, lastUpdate: Date) {
continue;
}
if (!updates.markerSets.has(set)) {
updates.markerSets.set(set, {
areaUpdates: [],
markerUpdates: [],
lineUpdates: [],
circleUpdates: [],
removed: false,
});
}
const markerSetUpdates = updates.markerSets.get(set),
update: DynmapUpdate = {
id: entry.id,
removed: entry.msg.endsWith('deleted'),
};
const update: any = {
id: entry.id,
removed: entry.msg.endsWith('deleted'),
set,
};
if (entry.msg.startsWith("set")) {
markerSetUpdates!.removed = update.removed;
markerSetUpdates!.payload = update.removed ? undefined : buildMarkerSet(set, entry);
} else 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));
if(!update.removed) {
update.payload = buildMarkerSet(set, entry);
}
} else if (entry.msg.startsWith("circle")) {
update.payload = update.removed ? undefined : buildCircle(entry);
markerSetUpdates!.circleUpdates.push(Object.freeze(update));
updates.markerSets.push(Object.freeze(update as DynmapMarkerSetUpdate));
} else {
if (entry.msg.startsWith("marker")) {
update.type = LiveAtlasMarkerType.POINT;
update.payload = update.removed ? undefined : buildMarker(entry);
} else if (entry.msg.startsWith("area")) {
update.type = LiveAtlasMarkerType.AREA;
update.payload = update.removed ? undefined : buildArea(entry);
} else if (entry.msg.startsWith("circle")) {
update.type = LiveAtlasMarkerType.CIRCLE;
update.payload = update.removed ? undefined : buildCircle(entry);
} else if (entry.msg.startsWith("line")) {
update.type = LiveAtlasMarkerType.LINE;
update.payload = update.removed ? undefined : buildLine(entry);
}
} else if (entry.msg.startsWith("line")) {
update.payload = update.removed ? undefined : buildLine(entry);
markerSetUpdates!.lineUpdates.push(Object.freeze(update));
updates.markers.push(Object.freeze(update as DynmapMarkerUpdate));
}
accepted++;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2021 James Lyne
* Copyright 2022 James Lyne
*
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
* These portions are Copyright 2020 Dynmap Contributors.
@ -17,69 +17,102 @@
* limitations under the License.
*/
import {LeafletMouseEvent, Marker} from "leaflet";
import {GenericIcon} from "@/leaflet/icon/GenericIcon";
import {GenericMarker} from "@/leaflet/marker/GenericMarker";
import {LiveAtlasPointMarker} from "@/index";
import {useStore} from "@/store";
import {ActionTypes} from "@/store/action-types";
import {DynmapMarkerUpdate} from "@/dynmap";
import {computed, watch} from "@vue/runtime-core";
import {ComputedRef} from "@vue/reactivity";
export const createMarker = (options: LiveAtlasPointMarker, converter: Function): Marker => {
const marker = new GenericMarker(converter(options.location), options);
export type LiveAtlasMarkerUpdateCallback = ((update: DynmapMarkerUpdate) => void);
marker.on('click', (e: LeafletMouseEvent) => {
if(!e.target.getPopup() || e.target.isPopupOpen()) {
e.target._map.panTo(e.target.getLatLng());
export enum LiveAtlasMarkerType {
POINT,
AREA,
LINE,
CIRCLE
}
let updateFrame = 0;
let pendingUpdates: ComputedRef;
const setHandlers: { [key:string]: Set<LiveAtlasMarkerUpdateCallback>} = {};
const typeHandlers: { [key:string]: Map<LiveAtlasMarkerType, Set<LiveAtlasMarkerUpdateCallback>>} = {};
export const startUpdateHandling = () => {
const store = useStore();
pendingUpdates = computed(() => store.state.pendingMarkerUpdates.length);
watch(pendingUpdates, (newValue, oldValue) => {
if(newValue && newValue > 0 && oldValue === 0 && !updateFrame) {
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
}
});
}
if(options.popup) {
marker.bindPopup(() => createPopup(options));
export const stopUpdateHandling = () => {
if(updateFrame) {
cancelAnimationFrame(updateFrame);
updateFrame = 0;
}
}
export const registerUpdateHandler = (callback: LiveAtlasMarkerUpdateCallback, set: string) => {
if(!setHandlers[set]) {
setHandlers[set] = new Set();
}
return marker;
};
setHandlers[set].add(callback);
}
export const updateMarker = (marker: Marker | undefined, options: LiveAtlasPointMarker, converter: Function): Marker => {
if (!marker) {
return createMarker(options, converter);
export const registerTypeUpdateHandler = (callback: LiveAtlasMarkerUpdateCallback, set: string, type: LiveAtlasMarkerType) => {
if(!typeHandlers[set]) {
typeHandlers[set] = new Map();
}
const oldLocation = marker.getLatLng(),
newLocation = converter(options.location);
if(typeHandlers[set].has(type)) {
typeHandlers[set].get(type)!.add(callback);
} else {
typeHandlers[set].set(type, new Set([callback]));
}
}
if(!oldLocation.equals(newLocation)) {
marker.setLatLng(newLocation);
export const unregisterUpdateHandler = (callback: LiveAtlasMarkerUpdateCallback, set: string) => {
if(!setHandlers[set]) {
return;
}
if(marker instanceof GenericMarker) {
const icon = marker.getIcon();
setHandlers[set].delete(callback);
}
if(icon && icon instanceof GenericIcon) {
icon.update({
icon: options.icon,
label: options.tooltip,
iconSize: options.dimensions,
isHtml: !!options.tooltipHTML,
});
export const unregisterTypeUpdateHandler = (callback: LiveAtlasMarkerUpdateCallback, set: string, type: LiveAtlasMarkerType) => {
if(typeHandlers[set]) {
return;
}
if(typeHandlers[set].has(type)) {
typeHandlers[set].get(type)!.delete(callback);
}
}
const handlePendingUpdates = async () => {
const store = useStore(),
updates = await store.dispatch(ActionTypes.POP_MARKER_UPDATES, 10);
for(const update of updates) {
if(setHandlers[update.set]) {
setHandlers[update.set].forEach(callback => callback(update));
}
if(typeHandlers[update.set] && typeHandlers[update.set].has(update.type)) {
typeHandlers[update.set].get(update.type)!.forEach(callback => callback(update));
}
}
marker.closePopup();
marker.unbindPopup();
if(options.popup) {
marker.bindPopup(() => createPopup(options));
if(pendingUpdates.value) {
// eslint-disable-next-line no-unused-vars
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
} else {
updateFrame = 0;
}
return marker;
};
const createPopup = (options: LiveAtlasPointMarker) => {
const popup = document.createElement('span');
if (options.popup) {
popup.classList.add('MarkerPopup');
popup.insertAdjacentHTML('afterbegin', options.popup);
}
return popup;
}