Move and rename player image util methods
This commit is contained in:
parent
f794dcb813
commit
f4481a1d6c
@ -29,12 +29,13 @@ import Sidebar from './components/Sidebar.vue';
|
|||||||
import ChatBox from './components/ChatBox.vue';
|
import ChatBox from './components/ChatBox.vue';
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import {ActionTypes} from "@/store/action-types";
|
import {ActionTypes} from "@/store/action-types";
|
||||||
import {clearHeadCache, parseUrl} from '@/util';
|
import {parseUrl} from '@/util';
|
||||||
import {hideSplash, showSplash, showSplashError} from '@/util/splash';
|
import {hideSplash, showSplash, showSplashError} from '@/util/splash';
|
||||||
import {MutationTypes} from "@/store/mutation-types";
|
import {MutationTypes} from "@/store/mutation-types";
|
||||||
import {LiveAtlasServerDefinition, LiveAtlasUIElement} from "@/index";
|
import {LiveAtlasServerDefinition, LiveAtlasUIElement} from "@/index";
|
||||||
import LoginModal from "@/components/login/LoginModal.vue";
|
import LoginModal from "@/components/login/LoginModal.vue";
|
||||||
import {notify} from "@kyvg/vue3-notification";
|
import {notify} from "@kyvg/vue3-notification";
|
||||||
|
import {clearPlayerImageCache} from "@/util/images";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'App',
|
name: 'App',
|
||||||
@ -171,7 +172,7 @@ export default defineComponent({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearHeadCache();
|
clearPlayerImageCache();
|
||||||
loadingAttempts.value = 0;
|
loadingAttempts.value = 0;
|
||||||
window.history.replaceState({}, '', newServer.id);
|
window.history.replaceState({}, '', newServer.id);
|
||||||
loadConfiguration();
|
loadConfiguration();
|
||||||
@ -201,7 +202,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
watch(playerImageUrl, () => {
|
watch(playerImageUrl, () => {
|
||||||
clearHeadCache();
|
clearPlayerImageCache();
|
||||||
});
|
});
|
||||||
|
|
||||||
handleUrl();
|
handleUrl();
|
||||||
|
@ -24,7 +24,7 @@ import {computed, defineComponent, watch} from "@vue/runtime-core";
|
|||||||
import {LiveAtlasPlayer} from "@/index";
|
import {LiveAtlasPlayer} from "@/index";
|
||||||
import {onMounted, ref} from "vue";
|
import {onMounted, ref} from "vue";
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import {getMinecraftHead} from "@/util";
|
import {getPlayerImage} from "@/util/images";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'PlayerImage',
|
name: 'PlayerImage',
|
||||||
@ -47,7 +47,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
if (imagesEnabled.value) {
|
if (imagesEnabled.value) {
|
||||||
try {
|
try {
|
||||||
const result = await getMinecraftHead(props.player, 'small');
|
const result = await getPlayerImage(props.player, 'small');
|
||||||
image.value = result.src;
|
image.value = result.src;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
|
6
src/index.d.ts
vendored
6
src/index.d.ts
vendored
@ -251,7 +251,7 @@ interface LiveAtlasCircleMarker extends LiveAtlasPathMarker {
|
|||||||
radius: PointTuple;
|
radius: PointTuple;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HeadQueueEntry {
|
interface PlayerImageQueueEntry {
|
||||||
cacheKey: string;
|
cacheKey: string;
|
||||||
name: string;
|
name: string;
|
||||||
uuid?: string;
|
uuid?: string;
|
||||||
@ -277,7 +277,7 @@ interface LiveAtlasComponentConfig {
|
|||||||
markers?: LiveAtlasPlayerMarkerConfig;
|
markers?: LiveAtlasPlayerMarkerConfig;
|
||||||
showImages: boolean;
|
showImages: boolean;
|
||||||
grayHiddenPlayers: boolean;
|
grayHiddenPlayers: boolean;
|
||||||
imageUrl: (entry: HeadQueueEntry) => string;
|
imageUrl: (entry: PlayerImageQueueEntry) => string;
|
||||||
};
|
};
|
||||||
coordinatesControl?: CoordinatesControlOptions;
|
coordinatesControl?: CoordinatesControlOptions;
|
||||||
clockControl?: ClockControlOptions;
|
clockControl?: ClockControlOptions;
|
||||||
@ -298,7 +298,7 @@ interface LiveAtlasPartialComponentConfig {
|
|||||||
markers?: LiveAtlasPlayerMarkerConfig;
|
markers?: LiveAtlasPlayerMarkerConfig;
|
||||||
showImages?: boolean;
|
showImages?: boolean;
|
||||||
grayHiddenPlayers?: boolean;
|
grayHiddenPlayers?: boolean;
|
||||||
imageUrl?: (entry: HeadQueueEntry) => string;
|
imageUrl?: (entry: PlayerImageQueueEntry) => string;
|
||||||
};
|
};
|
||||||
coordinatesControl?: CoordinatesControlOptions;
|
coordinatesControl?: CoordinatesControlOptions;
|
||||||
clockControl?: ClockControlOptions;
|
clockControl?: ClockControlOptions;
|
||||||
|
@ -18,9 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {BaseIconOptions, Icon, Layer, LayerOptions, Util} from 'leaflet';
|
import {BaseIconOptions, Icon, Layer, LayerOptions, Util} from 'leaflet';
|
||||||
import {getImagePixelSize, getMinecraftHead} from '@/util';
|
|
||||||
import defaultImage from '@/assets/images/player_face.png';
|
import defaultImage from '@/assets/images/player_face.png';
|
||||||
import {LiveAtlasPlayer, LiveAtlasPlayerImageSize} from "@/index";
|
import {LiveAtlasPlayer, LiveAtlasPlayerImageSize} from "@/index";
|
||||||
|
import {getImagePixelSize, getPlayerImage} from "@/util/images";
|
||||||
|
|
||||||
const playerImage: HTMLImageElement = document.createElement('img');
|
const playerImage: HTMLImageElement = document.createElement('img');
|
||||||
playerImage.src = defaultImage;
|
playerImage.src = defaultImage;
|
||||||
@ -122,7 +122,7 @@ export class PlayerIcon extends Layer implements Icon<PlayerIconOptions> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateImage() {
|
updateImage() {
|
||||||
getMinecraftHead(this._player, this.options.imageSize).then(head => {
|
getPlayerImage(this._player, this.options.imageSize).then(head => {
|
||||||
this._playerImage!.src = head.src;
|
this._playerImage!.src = head.src;
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ import {MutationTypes} from "@/store/mutation-types";
|
|||||||
import MapProvider from "@/providers/MapProvider";
|
import MapProvider from "@/providers/MapProvider";
|
||||||
import {
|
import {
|
||||||
getBoundsFromPoints,
|
getBoundsFromPoints,
|
||||||
getDefaultMinecraftHead,
|
|
||||||
getMiddle,
|
getMiddle,
|
||||||
guessWorldDimension,
|
guessWorldDimension,
|
||||||
runSandboxed,
|
runSandboxed,
|
||||||
@ -43,6 +42,7 @@ import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
|||||||
import {OverviewerProjection} from "@/leaflet/projection/OverviewerProjection";
|
import {OverviewerProjection} from "@/leaflet/projection/OverviewerProjection";
|
||||||
import {LiveAtlasMarkerType} from "@/util/markers";
|
import {LiveAtlasMarkerType} from "@/util/markers";
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
|
import {getDefaultPlayerImage} from "@/util/images";
|
||||||
|
|
||||||
export default class OverviewerMapProvider extends MapProvider {
|
export default class OverviewerMapProvider extends MapProvider {
|
||||||
private configurationAbort?: AbortController = undefined;
|
private configurationAbort?: AbortController = undefined;
|
||||||
@ -247,7 +247,7 @@ export default class OverviewerMapProvider extends MapProvider {
|
|||||||
//Not used by Overviewer
|
//Not used by Overviewer
|
||||||
players: {
|
players: {
|
||||||
markers: undefined,
|
markers: undefined,
|
||||||
imageUrl: getDefaultMinecraftHead,
|
imageUrl: getDefaultPlayerImage,
|
||||||
showImages: false,
|
showImages: false,
|
||||||
grayHiddenPlayers: false,
|
grayHiddenPlayers: false,
|
||||||
},
|
},
|
||||||
|
@ -32,12 +32,13 @@ import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
|||||||
import {MutationTypes} from "@/store/mutation-types";
|
import {MutationTypes} from "@/store/mutation-types";
|
||||||
import MapProvider from "@/providers/MapProvider";
|
import MapProvider from "@/providers/MapProvider";
|
||||||
import {ActionTypes} from "@/store/action-types";
|
import {ActionTypes} from "@/store/action-types";
|
||||||
import {getBoundsFromPoints, getDefaultMinecraftHead, getMiddle, stripHTML, titleColoursRegex} from "@/util";
|
import {getBoundsFromPoints, getMiddle, stripHTML, titleColoursRegex} from "@/util";
|
||||||
import {LiveAtlasMarkerType} from "@/util/markers";
|
import {LiveAtlasMarkerType} from "@/util/markers";
|
||||||
import {PointTuple} from "leaflet";
|
import {PointTuple} from "leaflet";
|
||||||
import ConfigurationError from "@/errors/ConfigurationError";
|
import ConfigurationError from "@/errors/ConfigurationError";
|
||||||
import {Pl3xmapTileLayer} from "@/leaflet/tileLayer/Pl3xmapTileLayer";
|
import {Pl3xmapTileLayer} from "@/leaflet/tileLayer/Pl3xmapTileLayer";
|
||||||
import {LiveAtlasTileLayer, LiveAtlasTileLayerOptions} from "@/leaflet/tileLayer/LiveAtlasTileLayer";
|
import {LiveAtlasTileLayer, LiveAtlasTileLayerOptions} from "@/leaflet/tileLayer/LiveAtlasTileLayer";
|
||||||
|
import {getDefaultPlayerImage} from "@/util/images";
|
||||||
|
|
||||||
export default class Pl3xmapMapProvider extends MapProvider {
|
export default class Pl3xmapMapProvider extends MapProvider {
|
||||||
private configurationAbort?: AbortController = undefined;
|
private configurationAbort?: AbortController = undefined;
|
||||||
@ -120,7 +121,7 @@ export default class Pl3xmapMapProvider extends MapProvider {
|
|||||||
components: {
|
components: {
|
||||||
players: {
|
players: {
|
||||||
markers: undefined,
|
markers: undefined,
|
||||||
imageUrl: getDefaultMinecraftHead,
|
imageUrl: getDefaultPlayerImage,
|
||||||
grayHiddenPlayers: true,
|
grayHiddenPlayers: true,
|
||||||
showImages: true,
|
showImages: true,
|
||||||
}
|
}
|
||||||
@ -214,7 +215,7 @@ export default class Pl3xmapMapProvider extends MapProvider {
|
|||||||
|
|
||||||
players: {
|
players: {
|
||||||
markers: undefined, //Configured per-world
|
markers: undefined, //Configured per-world
|
||||||
imageUrl: getDefaultMinecraftHead,
|
imageUrl: getDefaultPlayerImage,
|
||||||
|
|
||||||
//Not configurable
|
//Not configurable
|
||||||
showImages: true,
|
showImages: true,
|
||||||
|
@ -40,8 +40,9 @@ import {
|
|||||||
LiveAtlasUIModal,
|
LiveAtlasUIModal,
|
||||||
LiveAtlasSidebarSectionState, LiveAtlasMarker, LiveAtlasMapViewTarget
|
LiveAtlasSidebarSectionState, LiveAtlasMarker, LiveAtlasMapViewTarget
|
||||||
} from "@/index";
|
} from "@/index";
|
||||||
import {getDefaultMinecraftHead, getGlobalMessages} from "@/util";
|
import {getGlobalMessages} from "@/util";
|
||||||
import {getServerMapProvider} from "@/util/config";
|
import {getServerMapProvider} from "@/util/config";
|
||||||
|
import {getDefaultPlayerImage} from "@/util/images";
|
||||||
|
|
||||||
export type CurrentMapPayload = {
|
export type CurrentMapPayload = {
|
||||||
worldName: string;
|
worldName: string;
|
||||||
@ -559,7 +560,7 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
markers: undefined,
|
markers: undefined,
|
||||||
showImages: true,
|
showImages: true,
|
||||||
grayHiddenPlayers: true,
|
grayHiddenPlayers: true,
|
||||||
imageUrl: getDefaultMinecraftHead,
|
imageUrl: getDefaultPlayerImage,
|
||||||
};
|
};
|
||||||
state.components.coordinatesControl = undefined;
|
state.components.coordinatesControl = undefined;
|
||||||
state.components.clockControl = undefined;
|
state.components.clockControl = undefined;
|
||||||
|
@ -39,7 +39,8 @@ import {
|
|||||||
LiveAtlasMarker, LiveAtlasMapViewTarget
|
LiveAtlasMarker, LiveAtlasMapViewTarget
|
||||||
} from "@/index";
|
} from "@/index";
|
||||||
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
||||||
import {getDefaultMinecraftHead, getMessages} from "@/util";
|
import {getMessages} from "@/util";
|
||||||
|
import {getDefaultPlayerImage} from "@/util/images";
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
version: string;
|
version: string;
|
||||||
@ -162,7 +163,7 @@ export const state: State = {
|
|||||||
showImages: false,
|
showImages: false,
|
||||||
|
|
||||||
// (world-settings.x.player-tracker.heads-url in squaremap)
|
// (world-settings.x.player-tracker.heads-url in squaremap)
|
||||||
imageUrl: getDefaultMinecraftHead,
|
imageUrl: getDefaultPlayerImage,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Settings for coordinates control
|
// Settings for coordinates control
|
||||||
|
92
src/util.ts
92
src/util.ts
@ -14,29 +14,21 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import defaultImage from '@/assets/images/player_face.png';
|
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
||||||
import {
|
import {
|
||||||
Coordinate,
|
Coordinate,
|
||||||
HeadQueueEntry,
|
LiveAtlasBounds,
|
||||||
LiveAtlasBounds, LiveAtlasDimension,
|
LiveAtlasDimension,
|
||||||
LiveAtlasGlobalMessageConfig,
|
LiveAtlasGlobalMessageConfig,
|
||||||
LiveAtlasLocation,
|
LiveAtlasLocation,
|
||||||
LiveAtlasMessageConfig,
|
LiveAtlasMessageConfig,
|
||||||
LiveAtlasPlayer,
|
|
||||||
LiveAtlasPlayerImageSize,
|
|
||||||
} from "@/index";
|
} from "@/index";
|
||||||
import {notify} from "@kyvg/vue3-notification";
|
import {notify} from "@kyvg/vue3-notification";
|
||||||
import {globalMessages, serverMessages} from "../messages";
|
import {globalMessages, serverMessages} from "../messages";
|
||||||
|
|
||||||
const documentRange = document.createRange(),
|
const documentRange = document.createRange(),
|
||||||
brToSpaceRegex = /<br \/>/g,
|
brToSpaceRegex = /<br \/>/g;
|
||||||
headCache = new Map<string, HTMLImageElement>(),
|
|
||||||
headUnresolvedCache = new Map<string, Promise<HTMLImageElement>>(),
|
|
||||||
headsLoading = new Set<string>(),
|
|
||||||
|
|
||||||
headQueue: HeadQueueEntry[] = [];
|
|
||||||
|
|
||||||
export const titleColoursRegex = /§[0-9a-f]/ig;
|
export const titleColoursRegex = /§[0-9a-f]/ig;
|
||||||
export const netherWorldNameRegex = /[_\s]?nether([\s_]|$)/i;
|
export const netherWorldNameRegex = /[_\s]?nether([\s_]|$)/i;
|
||||||
@ -59,84 +51,6 @@ export const getMinecraftTime = (serverTime: number) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getImagePixelSize = (imageSize: LiveAtlasPlayerImageSize) => {
|
|
||||||
switch(imageSize) {
|
|
||||||
case 'large':
|
|
||||||
case 'body':
|
|
||||||
return 32;
|
|
||||||
|
|
||||||
case 'small':
|
|
||||||
default:
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getMinecraftHead = (player: LiveAtlasPlayer | string, size: LiveAtlasPlayerImageSize): Promise<HTMLImageElement> => {
|
|
||||||
const account = typeof player === 'string' ? player : player.name,
|
|
||||||
uuid = typeof player === 'string' ? undefined : player.uuid,
|
|
||||||
cacheKey = `${account}-${size}`;
|
|
||||||
|
|
||||||
if(headCache.has(cacheKey)) {
|
|
||||||
return Promise.resolve(headCache.get(cacheKey) as HTMLImageElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(headUnresolvedCache.has(cacheKey)) {
|
|
||||||
return headUnresolvedCache.get(cacheKey) as Promise<HTMLImageElement>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const promise = new Promise((resolve, reject) => {
|
|
||||||
const faceImage = new Image();
|
|
||||||
|
|
||||||
faceImage.onload = function() {
|
|
||||||
headCache.set(cacheKey, faceImage);
|
|
||||||
headsLoading.delete(cacheKey);
|
|
||||||
tickHeadQueue();
|
|
||||||
resolve(faceImage);
|
|
||||||
};
|
|
||||||
|
|
||||||
faceImage.onerror = function(e) {
|
|
||||||
console.warn(`Failed to retrieve face of ${account} with size ${size}!`);
|
|
||||||
headsLoading.delete(cacheKey);
|
|
||||||
tickHeadQueue();
|
|
||||||
reject(e);
|
|
||||||
};
|
|
||||||
|
|
||||||
headQueue.push({
|
|
||||||
name: account,
|
|
||||||
uuid,
|
|
||||||
size,
|
|
||||||
cacheKey,
|
|
||||||
image: faceImage,
|
|
||||||
});
|
|
||||||
}).finally(() => headUnresolvedCache.delete(cacheKey)) as Promise<HTMLImageElement>;
|
|
||||||
|
|
||||||
headUnresolvedCache.set(cacheKey, promise);
|
|
||||||
tickHeadQueue();
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getDefaultMinecraftHead = () => {
|
|
||||||
return defaultImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tickHeadQueue = () => {
|
|
||||||
if(headsLoading.size > 8 || !headQueue.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const head = headQueue.pop() as HeadQueueEntry;
|
|
||||||
|
|
||||||
headsLoading.add(head.cacheKey);
|
|
||||||
head.image.src = useStore().state.components.players.imageUrl(head);
|
|
||||||
|
|
||||||
tickHeadQueue();
|
|
||||||
}
|
|
||||||
|
|
||||||
export const clearHeadCache = () => {
|
|
||||||
headCache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
export const parseUrl = (url: URL) => {
|
export const parseUrl = (url: URL) => {
|
||||||
const query = new URLSearchParams(url.search),
|
const query = new URLSearchParams(url.search),
|
||||||
hash = url.hash.replace('#', '');
|
hash = url.hash.replace('#', '');
|
||||||
|
@ -32,8 +32,7 @@ import {
|
|||||||
} from "@/index";
|
} from "@/index";
|
||||||
import {getPoints} from "@/util/areas";
|
import {getPoints} from "@/util/areas";
|
||||||
import {
|
import {
|
||||||
decodeHTMLEntities, getBounds, getImagePixelSize,
|
decodeHTMLEntities, getBounds, getMiddle, guessWorldDimension,
|
||||||
getMiddle, guessWorldDimension,
|
|
||||||
stripHTML,
|
stripHTML,
|
||||||
titleColoursRegex
|
titleColoursRegex
|
||||||
} from "@/util";
|
} from "@/util";
|
||||||
@ -53,6 +52,7 @@ import {
|
|||||||
import {PointTuple} from "leaflet";
|
import {PointTuple} from "leaflet";
|
||||||
import {LiveAtlasMarkerType} from "@/util/markers";
|
import {LiveAtlasMarkerType} from "@/util/markers";
|
||||||
import {DynmapProjection} from "@/leaflet/projection/DynmapProjection";
|
import {DynmapProjection} from "@/leaflet/projection/DynmapProjection";
|
||||||
|
import {getImagePixelSize} from "@/util/images";
|
||||||
|
|
||||||
export function buildServerConfig(response: Options): LiveAtlasServerConfig {
|
export function buildServerConfig(response: Options): LiveAtlasServerConfig {
|
||||||
let title = 'Dynmap';
|
let title = 'Dynmap';
|
||||||
|
138
src/util/images.ts
Normal file
138
src/util/images.ts
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 James Lyne
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import defaultImage from "@/assets/images/player_face.png";
|
||||||
|
import {PlayerImageQueueEntry, LiveAtlasPlayer, LiveAtlasPlayerImageSize} from "@/index";
|
||||||
|
import {useStore} from "@/store";
|
||||||
|
|
||||||
|
const playerImageCache = new Map<string, HTMLImageElement>(),
|
||||||
|
playerImageUnresolvedCache = new Map<string, Promise<HTMLImageElement>>(),
|
||||||
|
playerImagesLoading = new Set<string>(),
|
||||||
|
|
||||||
|
playerImageQueue: PlayerImageQueueEntry[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the corresponding pixel size for the given {@see LiveAtlasPlayerImageSize}
|
||||||
|
* @param {LiveAtlasPlayerImageSize} imageSize The image size to get the pixel size for
|
||||||
|
* @returns The pixel size
|
||||||
|
*/
|
||||||
|
export const getImagePixelSize = (imageSize: LiveAtlasPlayerImageSize) => {
|
||||||
|
switch (imageSize) {
|
||||||
|
case 'large':
|
||||||
|
case 'body':
|
||||||
|
return 32;
|
||||||
|
|
||||||
|
case 'small':
|
||||||
|
default:
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@see HTMLImageElement} containing an image representing the given {@see LiveAtlasPlayer}
|
||||||
|
* at the given {@see LiveAtlasPlayerImageSize} and ensures it has loaded successfully
|
||||||
|
*
|
||||||
|
* If an image has previously been loaded for the same player and image size, a cached copy of the image element
|
||||||
|
* will be returned; Otherwise an attempt will be made to load the player image from the URL specified by the current
|
||||||
|
* {@see LiveAtlasMapProvider}
|
||||||
|
*
|
||||||
|
* The number of concurrent image loads is limited and additional loads will be queued. If this method is called
|
||||||
|
* with the same player and image size multiple times, the load will only be queued once and the same element will be
|
||||||
|
* returned for all calls.
|
||||||
|
*
|
||||||
|
* @param {LiveAtlasPlayer} player The player to retrieve the image for
|
||||||
|
* @param {LiveAtlasPlayerImageSize} size The image size to retrieve
|
||||||
|
* @returns {Promise<HTMLImageElement>} A promise which will resolve to a {@see HTMLImageElement} with the loaded player
|
||||||
|
* image as the src. The promise will reject if the image fails to load
|
||||||
|
*/
|
||||||
|
export const getPlayerImage = (player: LiveAtlasPlayer | string, size: LiveAtlasPlayerImageSize): Promise<HTMLImageElement> => {
|
||||||
|
const account = typeof player === 'string' ? player : player.name,
|
||||||
|
uuid = typeof player === 'string' ? undefined : player.uuid,
|
||||||
|
cacheKey = `${account}-${size}`;
|
||||||
|
|
||||||
|
if (playerImageCache.has(cacheKey)) {
|
||||||
|
return Promise.resolve(playerImageCache.get(cacheKey) as HTMLImageElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (playerImageUnresolvedCache.has(cacheKey)) {
|
||||||
|
return playerImageUnresolvedCache.get(cacheKey) as Promise<HTMLImageElement>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const promise = new Promise((resolve, reject) => {
|
||||||
|
const faceImage = new Image();
|
||||||
|
|
||||||
|
faceImage.onload = function () {
|
||||||
|
playerImageCache.set(cacheKey, faceImage);
|
||||||
|
playerImagesLoading.delete(cacheKey);
|
||||||
|
tickPlayerImageQueue();
|
||||||
|
resolve(faceImage);
|
||||||
|
};
|
||||||
|
|
||||||
|
faceImage.onerror = function (e) {
|
||||||
|
console.warn(`Failed to retrieve face of ${account} with size ${size}!`);
|
||||||
|
playerImagesLoading.delete(cacheKey);
|
||||||
|
tickPlayerImageQueue();
|
||||||
|
reject(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
playerImageQueue.push({
|
||||||
|
name: account,
|
||||||
|
uuid,
|
||||||
|
size,
|
||||||
|
cacheKey,
|
||||||
|
image: faceImage,
|
||||||
|
});
|
||||||
|
}).finally(() => playerImageUnresolvedCache.delete(cacheKey)) as Promise<HTMLImageElement>;
|
||||||
|
|
||||||
|
playerImageUnresolvedCache.set(cacheKey, promise);
|
||||||
|
tickPlayerImageQueue();
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default "Steve" player image. This image can be used as a placeholder whilst waiting for
|
||||||
|
* {@see getPlayerImage} to complete
|
||||||
|
* @returns The default player image
|
||||||
|
*/
|
||||||
|
export const getDefaultPlayerImage = () => {
|
||||||
|
return defaultImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ticks the player image load queue, starting additional image loads if any are queued and the concurrent load limit
|
||||||
|
* hasn't been hit
|
||||||
|
*/
|
||||||
|
const tickPlayerImageQueue = () => {
|
||||||
|
if (playerImagesLoading.size > 8 || !playerImageQueue.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = playerImageQueue.pop() as PlayerImageQueueEntry;
|
||||||
|
|
||||||
|
playerImagesLoading.add(image.cacheKey);
|
||||||
|
image.image.src = useStore().state.components.players.imageUrl(image);
|
||||||
|
|
||||||
|
tickPlayerImageQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the player image cache
|
||||||
|
* Future calls to {@see getPlayerImage} will result in fresh image loads
|
||||||
|
*/
|
||||||
|
export const clearPlayerImageCache = () => {
|
||||||
|
playerImageCache.clear();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user