diff --git a/src/index.d.ts b/src/index.d.ts index 496abed..419a78d 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -151,11 +151,15 @@ interface LiveAtlasParsedUrl { interface LiveAtlasMapProvider { loadServerConfiguration(): Promise; - loadWorldConfiguration(world: LiveAtlasWorldDefinition): Promise; + populateWorld(world: LiveAtlasWorldDefinition): Promise; startUpdates(): void; stopUpdates(): void; sendChatMessage(message: string): void; destroy(): void; + + getPlayerHeadUrl(entry: HeadQueueEntry): string; + getTilesUrl(): string; + getMarkerIconUrl(icon: string): string; } interface LiveAtlasMarkerSet { @@ -217,3 +221,11 @@ interface LiveAtlasCircle { maxZoom?: number; popupContent?: string; } + +interface HeadQueueEntry { + cacheKey: string; + name: string; + uuid?: string; + size: string; + image: HTMLImageElement; +} diff --git a/src/leaflet/icon/GenericIcon.ts b/src/leaflet/icon/GenericIcon.ts index 68e736c..c4dd3be 100644 --- a/src/leaflet/icon/GenericIcon.ts +++ b/src/leaflet/icon/GenericIcon.ts @@ -60,7 +60,7 @@ export class GenericIcon extends DivIcon { } const div = markerContainer.cloneNode(false) as HTMLDivElement, - url = `${useStore().getters.serverConfig.dynmap.markers}_markers_/${this.options.icon}.png`, + url = useStore().state.currentMapProvider!.getMarkerIconUrl(this.options.icon), size = point(this.options.iconSize as PointExpression); this._image = markerIcon.cloneNode(false) as HTMLImageElement; @@ -96,7 +96,7 @@ export class GenericIcon extends DivIcon { update(options: GenericIconOptions) { if(this._image && options.icon !== this.options.icon) { - this._image!.src = `${useStore().getters.serverConfig.dynmap.markers}_markers_/${options.icon}.png`; + this._image!.src = useStore().state.currentMapProvider!.getMarkerIconUrl(this.options.icon); this.options.icon = options.icon; } diff --git a/src/leaflet/tileLayer/DynmapTileLayer.ts b/src/leaflet/tileLayer/DynmapTileLayer.ts index 6c9bf07..6dd66ae 100644 --- a/src/leaflet/tileLayer/DynmapTileLayer.ts +++ b/src/leaflet/tileLayer/DynmapTileLayer.ts @@ -62,6 +62,7 @@ export class DynmapTileLayer extends TileLayer { private readonly _loadQueue: DynmapTileElement[] = []; private readonly _loadingTiles: Set = Object.seal(new Set()); private readonly _tileTemplate: DynmapTileElement; + private readonly _baseUrl: string; declare readonly options: DynmapTileLayerOptions; constructor(options: DynmapTileLayerOptions) { @@ -85,6 +86,7 @@ export class DynmapTileLayer extends TileLayer { this._tileTemplate.alt = ''; this._tileTemplate.tileName = ''; this._tileTemplate.setAttribute('role', 'presentation'); + this._baseUrl = store.state.currentMapProvider!.getTilesUrl(); Object.seal(this._tileTemplate); @@ -110,7 +112,7 @@ export class DynmapTileLayer extends TileLayer { if (!url) { const path = escape(`${this._mapSettings.world.name}/${name}`); - url = `${store.getters.serverConfig.dynmap.tiles}${path}`; + url = `${this._baseUrl}${path}`; if(typeof timestamp !== 'undefined') { url += (url.indexOf('?') === -1 ? `?timestamp=${timestamp}` : `×tamp=${timestamp}`); diff --git a/src/providers/DynmapMapProvider.ts b/src/providers/DynmapMapProvider.ts index 2ade58a..d1c786d 100644 --- a/src/providers/DynmapMapProvider.ts +++ b/src/providers/DynmapMapProvider.ts @@ -15,6 +15,7 @@ */ import { + HeadQueueEntry, LiveAtlasArea, LiveAtlasCircle, LiveAtlasDimension, @@ -582,8 +583,8 @@ export default class DynmapMapProvider extends MapProvider { return updates; } - private async getMarkerSets(world: string): Promise> { - const url = `${useStore().getters.serverConfig.dynmap.markers}_markers_/marker_${world}.json`; + private async getMarkerSets(world: LiveAtlasWorldDefinition): Promise> { + const url = `${this.config.dynmap!.markers}_markers_/marker_${world.name}.json`; if(this.markersAbort) { this.markersAbort.abort(); @@ -619,7 +620,6 @@ export default class DynmapMapProvider extends MapProvider { return sets; } - async loadServerConfiguration(): Promise { if(this.configurationAbort) { this.configurationAbort.abort(); @@ -627,7 +627,7 @@ export default class DynmapMapProvider extends MapProvider { this.configurationAbort = new AbortController(); - const response = await DynmapMapProvider.fetchJSON(useStore().getters.serverConfig.dynmap.configuration, this.configurationAbort.signal); + const response = await DynmapMapProvider.fetchJSON(this.config.dynmap!.configuration, this.configurationAbort.signal); if (response.error === 'login-required') { throw new Error("Login required"); @@ -647,14 +647,14 @@ export default class DynmapMapProvider extends MapProvider { this.store.commit(MutationTypes.SET_LOGGED_IN, response.loggedin || false); } - async loadWorldConfiguration(): Promise { - const markerSets = await this.getMarkerSets(this.store.state.currentWorld!.name); + async populateWorld(world: LiveAtlasWorldDefinition): Promise { + const markerSets = await this.getMarkerSets(world); useStore().commit(MutationTypes.SET_MARKER_SETS, markerSets); } private async getUpdate(): Promise { - let url = useStore().getters.serverConfig.dynmap.update; + let url = this.config.dynmap!.update; url = url.replace('{world}', this.store.state.currentWorld!.name); url = url.replace('{timestamp}', this.updateTimestamp.getTime().toString()); @@ -729,7 +729,7 @@ export default class DynmapMapProvider extends MapProvider { return Promise.reject(store.state.messages.chatErrorDisabled); } - return fetch(useStore().getters.serverConfig.dynmap.sendmessage, { + return fetch(this.config.dynmap!.sendmessage, { method: 'POST', body: JSON.stringify({ name: null, @@ -789,7 +789,23 @@ export default class DynmapMapProvider extends MapProvider { this.updateTimeout = 0; } + getTilesUrl(): string { + return this.config.dynmap!.tiles; + } + + getPlayerHeadUrl(head: HeadQueueEntry): string { + const icon = (head.size === 'body') ? `faces/body/${head.name}.png` :`faces/${head.size}x${head.size}/${head.name}.png` + + return this.getMarkerIconUrl(icon); + } + + getMarkerIconUrl(icon: string): string { + return `${this.config.dynmap!.markers}_markers_/${icon}.png`; + } + destroy() { + super.destroy(); + if(this.configurationAbort) { this.configurationAbort.abort(); } diff --git a/src/providers/MapProvider.ts b/src/providers/MapProvider.ts index 8e09b9e..2b6dd44 100644 --- a/src/providers/MapProvider.ts +++ b/src/providers/MapProvider.ts @@ -1,27 +1,43 @@ -import {LiveAtlasMapProvider, LiveAtlasServerDefinition, LiveAtlasWorldDefinition} from "@/index"; +import { + HeadQueueEntry, + LiveAtlasMapProvider, + LiveAtlasServerDefinition, + LiveAtlasWorldDefinition +} from "@/index"; import {useStore} from "@/store"; -import {watch} from "vue"; -import {computed} from "@vue/runtime-core"; +import {computed, watch} from "@vue/runtime-core"; +import {WatchStopHandle} from "vue"; export default abstract class MapProvider implements LiveAtlasMapProvider { protected readonly store = useStore(); + protected readonly config: LiveAtlasServerDefinition; + private readonly currentWorldUnwatch: WatchStopHandle; protected constructor(config: LiveAtlasServerDefinition) { + this.config = config; const currentWorld = computed(() => this.store.state.currentWorld); - watch(currentWorld, (newValue) => { - if(newValue) { - this.loadWorldConfiguration(newValue); + this.currentWorldUnwatch = watch(currentWorld, (newValue) => { + if (newValue) { + this.populateWorld(newValue); } }); } abstract loadServerConfiguration(): Promise; - abstract loadWorldConfiguration(world: LiveAtlasWorldDefinition): Promise; + abstract populateWorld(world: LiveAtlasWorldDefinition): Promise; abstract sendChatMessage(message: string): void; + abstract startUpdates(): void; abstract stopUpdates(): void; - abstract destroy(): void; + + abstract getPlayerHeadUrl(head: HeadQueueEntry): string; + abstract getTilesUrl(): string; + abstract getMarkerIconUrl(icon: string): string; + + destroy() { + this.currentWorldUnwatch(); + } protected static async fetchJSON(url: string, signal: AbortSignal) { let response, json; diff --git a/src/store/getters.ts b/src/store/getters.ts index a39a1d6..af47678 100644 --- a/src/store/getters.ts +++ b/src/store/getters.ts @@ -17,7 +17,6 @@ import {GetterTree} from "vuex"; import {State} from "@/store/state"; import {getMinecraftTime, getUrlForLocation} from "@/util"; -import {LiveAtlasDynmapServerDefinition} from "@/index"; export type Getters = { playerMarkersEnabled(state: State): boolean; @@ -26,7 +25,6 @@ export type Getters = { night(state: State): boolean; mapBackground(state: State, getters: GetterTree & Getters): string; url(state: State, getters: GetterTree & Getters): string; - serverConfig(state: State, getters: GetterTree & Getters): LiveAtlasDynmapServerDefinition; } export const getters: GetterTree & Getters = { @@ -74,12 +72,4 @@ export const getters: GetterTree & Getters = { return getUrlForLocation(state.currentMap, {x,y,z}, zoom); }, - - serverConfig(state: State): LiveAtlasDynmapServerDefinition { - if(!state.currentServer) { - throw RangeError("No current server"); - } - - return state.currentServer as LiveAtlasDynmapServerDefinition; - }, } diff --git a/src/util.ts b/src/util.ts index 8b0283c..ee38064 100644 --- a/src/util.ts +++ b/src/util.ts @@ -16,14 +16,7 @@ import {useStore} from "@/store"; import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition"; -import {LiveAtlasPlayer} from "@/index"; - -interface HeadQueueEntry { - cacheKey: string; - account: string; - size: string; - image: HTMLImageElement; -} +import {HeadQueueEntry, LiveAtlasPlayer} from "@/index"; const headCache = new Map(), headUnresolvedCache = new Map>(), @@ -54,6 +47,7 @@ export const getMinecraftTime = (serverTime: number) => { export const getMinecraftHead = (player: LiveAtlasPlayer | string, size: string): Promise => { const account = typeof player === 'string' ? player : player.name, + uuid = typeof player === 'string' ? undefined : player.uuid, cacheKey = `${account}-${size}`; if(headCache.has(cacheKey)) { @@ -82,7 +76,8 @@ export const getMinecraftHead = (player: LiveAtlasPlayer | string, size: string) }; headQueue.push({ - account, + name: account, + uuid, size, cacheKey, image: faceImage, @@ -100,23 +95,14 @@ const tickHeadQueue = () => { return; } - const head = headQueue.pop() as HeadQueueEntry, - src = (head.size === 'body') ? `faces/body/${head.account}.png` :`faces/${head.size}x${head.size}/${head.account}.png`; + const head = headQueue.pop() as HeadQueueEntry; headsLoading.add(head.cacheKey); - head.image.src = concatURL(useStore().getters.serverConfig.dynmap.markers, src); + head.image.src = useStore().state.currentMapProvider!.getPlayerHeadUrl(head); tickHeadQueue(); } -export const concatURL = (base: string, addition: string) => { - if(base.indexOf('?') >= 0) { - return base + escape(addition); - } - - return base + addition; -} - export const getPointConverter = () => { const map = useStore().state.currentMap;