Merge branch 'pl3xmap'
# Conflicts: # src/api.ts # src/components/Map.vue # src/components/map/layer/MapLayer.vue # src/components/map/layer/MarkerSetLayer.vue # src/components/map/vector/Areas.vue # src/components/map/vector/Circles.vue # src/components/map/vector/Lines.vue # src/components/map/vector/Markers.vue # src/components/sidebar/WorldListItem.vue # src/dynmap.d.ts # src/index.d.ts # src/leaflet/icon/GenericIcon.ts # src/leaflet/layer/LiveAtlasLayerGroup.ts # src/leaflet/tileLayer/DynmapTileLayer.ts # src/leaflet/vector/LiveAtlasPolygon.ts # src/leaflet/vector/LiveAtlasPolyline.ts # src/model/LiveAtlasMapDefinition.ts # src/model/LiveAtlasProjection.ts # src/store/actions.ts # src/store/getters.ts # src/store/state.ts # src/util.ts # src/util/areas.ts # src/util/circles.ts # src/util/lines.ts # src/util/markers.ts
This commit is contained in:
commit
e07ac55c33
6
.idea/copyright/Dynmap.xml
Normal file
6
.idea/copyright/Dynmap.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<component name="CopyrightManager">
|
||||||
|
<copyright>
|
||||||
|
<option name="notice" value="Copyright &#36;today.year James Lyne Some portions of this file were taken from https://github.com/webbukkit/dynmap. These portions are Copyright 2020 Dynmap Contributors. 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." />
|
||||||
|
<option name="myName" value="Dynmap" />
|
||||||
|
</copyright>
|
||||||
|
</component>
|
6
.idea/copyright/Original.xml
Normal file
6
.idea/copyright/Original.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<component name="CopyrightManager">
|
||||||
|
<copyright>
|
||||||
|
<option name="notice" value="Copyright &#36;today.year 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." />
|
||||||
|
<option name="myName" value="Original" />
|
||||||
|
</copyright>
|
||||||
|
</component>
|
8
.idea/copyright/profiles_settings.xml
Normal file
8
.idea/copyright/profiles_settings.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<component name="CopyrightManager">
|
||||||
|
<settings>
|
||||||
|
<module2copyright>
|
||||||
|
<element module="Dynmap" copyright="Dynmap" />
|
||||||
|
<element module="Original" copyright="Original" />
|
||||||
|
</module2copyright>
|
||||||
|
</settings>
|
||||||
|
</component>
|
3
.idea/scopes/Dynmap.xml
Normal file
3
.idea/scopes/Dynmap.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<component name="DependencyValidationManager">
|
||||||
|
<scope name="Dynmap" pattern="file:src/leaflet/tileLayer/DynmapTileLayer.ts||file:src/leaflet/control/ClockControl.ts||file:src/leaflet/control/CoordinatesControl.ts||file:src/leaflet/control/LinkControl.ts||file:src/util/areas.ts||file:src/util/circles.ts||file:src/util/lines.ts||file:src/util/markers.ts||file:src/leaflet/control/LiveAtlasLayerControl.ts||file:src/leaflet/control/LogoControl.ts||file:src/leaflet/icon/PlayerIcon.ts||file:src/leaflet/icon/GenericIcon.ts||file:src/model/LiveAtlasProjection.ts||file:src/scss/style.scss" />
|
||||||
|
</component>
|
3
.idea/scopes/Original.xml
Normal file
3
.idea/scopes/Original.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<component name="DependencyValidationManager">
|
||||||
|
<scope name="Original" pattern="!file:src/leaflet/control/ClockControl.ts&&!file:src/leaflet/control/CoordinatesControl.ts&&!file:src/leaflet/control/LinkControl.ts&&!file:src/leaflet/control/LogoControl.ts&&!file:src/leaflet/icon/PlayerIcon.ts&&!file:src/leaflet/icon/GenericIcon.ts&&!file:src/leaflet/tileLayer/DynmapTileLayer.ts&&!file:src/util/areas.ts&&!file:src/util/circles.ts&&!file:src/util/lines.ts&&!file:src/util/markers.ts&&!file[LiveAtlas]:standalone/*&&!file:src/model/LiveAtlasProjection.ts&&!file:src/leaflet/control/LiveAtlasLayerControl.ts&&!file[LiveAtlas]:patches/*&&!file[LiveAtlas]:public/*&&!file[LiveAtlas]:.idea/*&&!file[LiveAtlas]:.idea//*&&!file[LiveAtlas]:patches//*&&!file[LiveAtlas]:public//*&&!file[LiveAtlas]:standalone//*&&!file:FUNDING.yml&&!file:README.md&&!file:tsconfig.json&&!file:.gitignore&&!file:.env&&!file:LICENSE.md&&!file:package-lock.json&&!file:package.json&&!file:vite.config.ts&&!file:index.html&&!file:src/leaflet/control/LoadingControl.ts&&!file:src/scss/style.scss" />
|
||||||
|
</component>
|
66
src/App.vue
66
src/App.vue
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -43,21 +43,19 @@ export default defineComponent({
|
|||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const store = useStore(),
|
const store = useStore(),
|
||||||
updateInterval = computed(() => store.state.configuration.updateInterval),
|
|
||||||
title = computed(() => store.state.configuration.title),
|
title = computed(() => store.state.configuration.title),
|
||||||
currentUrl = computed(() => store.getters.url),
|
currentUrl = computed(() => store.getters.url),
|
||||||
currentServer = computed(() => store.state.currentServer),
|
currentServer = computed(() => store.state.currentServer),
|
||||||
configurationHash = computed(() => store.state.configurationHash),
|
configurationHash = computed(() => store.state.configurationHash),
|
||||||
chatBoxEnabled = computed(() => store.state.components.chatBox),
|
chatBoxEnabled = computed(() => store.state.components.chatBox),
|
||||||
chatVisible = computed(() => store.state.ui.visibleElements.has('chat')),
|
chatVisible = computed(() => store.state.ui.visibleElements.has('chat')),
|
||||||
updatesEnabled = ref(false),
|
|
||||||
updateTimeout = ref(0),
|
|
||||||
configAttempts = ref(0),
|
configAttempts = ref(0),
|
||||||
|
|
||||||
loadConfiguration = async () => {
|
loadConfiguration = async () => {
|
||||||
try {
|
try {
|
||||||
await store.dispatch(ActionTypes.LOAD_CONFIGURATION, undefined);
|
await store.dispatch(ActionTypes.LOAD_CONFIGURATION, undefined);
|
||||||
startUpdates();
|
await store.dispatch(ActionTypes.START_UPDATES, undefined);
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
hideSplash();
|
hideSplash();
|
||||||
|
|
||||||
@ -80,36 +78,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
startUpdates = () => {
|
|
||||||
updatesEnabled.value = true;
|
|
||||||
update();
|
|
||||||
},
|
|
||||||
|
|
||||||
update = async () => {
|
|
||||||
//TODO: Error notification for repeated failures?
|
|
||||||
try {
|
|
||||||
await store.dispatch(ActionTypes.GET_UPDATE, undefined);
|
|
||||||
} finally {
|
|
||||||
if(updatesEnabled.value) {
|
|
||||||
if(updateTimeout.value) {
|
|
||||||
clearTimeout(updateTimeout.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTimeout.value = setTimeout(() => update(), updateInterval.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
stopUpdates = () => {
|
|
||||||
updatesEnabled.value = false;
|
|
||||||
|
|
||||||
if (updateTimeout.value) {
|
|
||||||
clearTimeout(updateTimeout.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTimeout.value = 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
handleUrl = () => {
|
handleUrl = () => {
|
||||||
const parsedUrl = parseUrl();
|
const parsedUrl = parseUrl();
|
||||||
|
|
||||||
@ -174,7 +142,6 @@ export default defineComponent({
|
|||||||
watch(currentUrl, (url) => window.history.replaceState({}, '', url));
|
watch(currentUrl, (url) => window.history.replaceState({}, '', url));
|
||||||
watch(currentServer, (newServer?: LiveAtlasServerDefinition) => {
|
watch(currentServer, (newServer?: LiveAtlasServerDefinition) => {
|
||||||
showSplash();
|
showSplash();
|
||||||
stopUpdates();
|
|
||||||
|
|
||||||
if(!newServer) {
|
if(!newServer) {
|
||||||
return;
|
return;
|
||||||
@ -182,6 +149,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
//Cleanup
|
//Cleanup
|
||||||
store.commit(MutationTypes.CLEAR_PLAYERS, undefined);
|
store.commit(MutationTypes.CLEAR_PLAYERS, undefined);
|
||||||
|
store.commit(MutationTypes.SET_MAX_PLAYERS, 0);
|
||||||
store.commit(MutationTypes.CLEAR_CURRENT_MAP, undefined);
|
store.commit(MutationTypes.CLEAR_CURRENT_MAP, undefined);
|
||||||
store.commit(MutationTypes.CLEAR_PARSED_URL, undefined);
|
store.commit(MutationTypes.CLEAR_PARSED_URL, undefined);
|
||||||
store.commit(MutationTypes.CLEAR_WORLDS, undefined);
|
store.commit(MutationTypes.CLEAR_WORLDS, undefined);
|
||||||
@ -190,17 +158,17 @@ export default defineComponent({
|
|||||||
window.history.replaceState({}, '', newServer.id);
|
window.history.replaceState({}, '', newServer.id);
|
||||||
loadConfiguration();
|
loadConfiguration();
|
||||||
}, {deep: true});
|
}, {deep: true});
|
||||||
watch(configurationHash, (newHash, oldHash) => {
|
watch(configurationHash, async (newHash, oldHash) => {
|
||||||
if(newHash && oldHash) {
|
if(newHash && oldHash) {
|
||||||
showSplash();
|
showSplash();
|
||||||
stopUpdates();
|
|
||||||
store.commit(MutationTypes.CLEAR_PARSED_URL, undefined);
|
store.commit(MutationTypes.CLEAR_PARSED_URL, undefined);
|
||||||
loadConfiguration();
|
await store.dispatch(ActionTypes.STOP_UPDATES, undefined);
|
||||||
|
await loadConfiguration();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => loadConfiguration());
|
onMounted(() => loadConfiguration());
|
||||||
onBeforeUnmount(() => stopUpdates());
|
onBeforeUnmount(() => store.dispatch(ActionTypes.STOP_UPDATES, undefined));
|
||||||
|
|
||||||
handleUrl();
|
handleUrl();
|
||||||
onResize();
|
onResize();
|
||||||
|
777
src/api.ts
777
src/api.ts
@ -1,777 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 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 {
|
|
||||||
DynmapArea,
|
|
||||||
DynmapChat,
|
|
||||||
DynmapCircle,
|
|
||||||
DynmapComponentConfig,
|
|
||||||
DynmapConfigurationResponse,
|
|
||||||
DynmapLine,
|
|
||||||
DynmapMarker,
|
|
||||||
DynmapMarkerSet,
|
|
||||||
DynmapMarkerSetUpdates,
|
|
||||||
DynmapPlayer,
|
|
||||||
DynmapServerConfig,
|
|
||||||
DynmapTileUpdate,
|
|
||||||
DynmapUpdate,
|
|
||||||
DynmapUpdateResponse,
|
|
||||||
DynmapUpdates
|
|
||||||
} from "@/dynmap";
|
|
||||||
import {useStore} from "@/store";
|
|
||||||
import ChatError from "@/errors/ChatError";
|
|
||||||
import {LiveAtlasDimension, LiveAtlasServerMessageConfig, LiveAtlasWorldDefinition} from "@/index";
|
|
||||||
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
|
||||||
|
|
||||||
const titleColours = /§[0-9a-f]/ig,
|
|
||||||
netherWorldName = /_?nether(_|$)/i,
|
|
||||||
endWorldName = /(^|_)end(_|$)/i;
|
|
||||||
|
|
||||||
function buildServerConfig(response: any): DynmapServerConfig {
|
|
||||||
return {
|
|
||||||
version: response.dynmapversion || '',
|
|
||||||
grayHiddenPlayers: response.grayplayerswhenhidden || false,
|
|
||||||
defaultMap: response.defaultmap || undefined,
|
|
||||||
defaultWorld: response.defaultworld || undefined,
|
|
||||||
defaultZoom: response.defaultzoom || 0,
|
|
||||||
followMap: response.followmap || undefined,
|
|
||||||
followZoom: response.followzoom || 0,
|
|
||||||
updateInterval: response.updaterate || 3000,
|
|
||||||
showLayerControl: response.showlayercontrol && response.showlayercontrol !== 'false', //Sent as a string for some reason
|
|
||||||
title: response.title.replace(titleColours, '') || 'Dynmap',
|
|
||||||
loginEnabled: response['login-enabled'] || false,
|
|
||||||
maxPlayers: response.maxcount || 0,
|
|
||||||
expandUI: response.sidebaropened && response.sidebaropened !== 'false', //Sent as a string for some reason
|
|
||||||
hash: response.confighash || 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildMessagesConfig(response: any): LiveAtlasServerMessageConfig {
|
|
||||||
return {
|
|
||||||
chatPlayerJoin: response.joinmessage || '',
|
|
||||||
chatPlayerQuit: response.quitmessage || '',
|
|
||||||
chatAnonymousJoin: response['msg-hiddennamejoin'] || '',
|
|
||||||
chatAnonymousQuit: response['msg-hiddennamequit'] || '',
|
|
||||||
chatErrorNotAllowed: response['msg-chatnotallowed'] || '',
|
|
||||||
chatErrorRequiresLogin: response['msg-chatrequireslogin'] || '',
|
|
||||||
chatErrorCooldown: response.spammessage || '',
|
|
||||||
worldsHeading: response['msg-maptypes'] || '',
|
|
||||||
playersHeading: response['msg-players'] || '',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildWorlds(response: any): Array<LiveAtlasWorldDefinition> {
|
|
||||||
const worlds: Map<string, LiveAtlasWorldDefinition> = new Map<string, LiveAtlasWorldDefinition>();
|
|
||||||
|
|
||||||
//Get all the worlds first so we can handle append_to_world properly
|
|
||||||
(response.worlds || []).forEach((world: any) => {
|
|
||||||
let worldType: LiveAtlasDimension = 'overworld';
|
|
||||||
|
|
||||||
if (netherWorldName.test(world.name) || (world.name == 'DIM-1')) {
|
|
||||||
worldType = 'nether';
|
|
||||||
} else if (endWorldName.test(world.name) || (world.name == 'DIM1')) {
|
|
||||||
worldType = 'end';
|
|
||||||
}
|
|
||||||
|
|
||||||
worlds.set(world.name, {
|
|
||||||
seaLevel: world.sealevel || 64,
|
|
||||||
name: world.name,
|
|
||||||
dimension: worldType,
|
|
||||||
protected: world.protected || false,
|
|
||||||
title: world.title || '',
|
|
||||||
height: world.height || 256,
|
|
||||||
center: {
|
|
||||||
x: world.center.x || 0,
|
|
||||||
y: world.center.y || 0,
|
|
||||||
z: world.center.z || 0
|
|
||||||
},
|
|
||||||
maps: new Map(),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
(response.worlds || []).forEach((world: any) => {
|
|
||||||
(world.maps || []).forEach((map: any) => {
|
|
||||||
const worldName = map.append_to_world || world.name,
|
|
||||||
w = worlds.get(worldName);
|
|
||||||
|
|
||||||
if(!w) {
|
|
||||||
console.warn(`Ignoring map '${map.name}' associated with non-existent world '${worldName}'`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
w.maps.set(map.name, new LiveAtlasMapDefinition({
|
|
||||||
world: world, //Ignore append_to_world here otherwise things break
|
|
||||||
background: map.background || '#000000',
|
|
||||||
backgroundDay: map.backgroundday || '#000000',
|
|
||||||
backgroundNight: map.backgroundnight || '#000000',
|
|
||||||
icon: map.icon || undefined,
|
|
||||||
imageFormat: map['image-format'] || 'png',
|
|
||||||
name: map.name || '(Unnamed map)',
|
|
||||||
nightAndDay: map.nightandday || false,
|
|
||||||
prefix: map.prefix || '',
|
|
||||||
protected: map.protected || false,
|
|
||||||
title: map.title || '',
|
|
||||||
mapToWorld: map.maptoworld || undefined,
|
|
||||||
worldToMap: map.worldtomap || undefined,
|
|
||||||
nativeZoomLevels: map.mapzoomout || 1,
|
|
||||||
extraZoomLevels: map.mapzoomin || 0
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return Array.from(worlds.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildComponents(response: any): DynmapComponentConfig {
|
|
||||||
const components: DynmapComponentConfig = {
|
|
||||||
markers: {
|
|
||||||
showLabels: false,
|
|
||||||
},
|
|
||||||
chatBox: undefined,
|
|
||||||
chatBalloons: false,
|
|
||||||
playerMarkers: undefined,
|
|
||||||
coordinatesControl: undefined,
|
|
||||||
linkControl: false,
|
|
||||||
clockControl: undefined,
|
|
||||||
logoControls: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
(response.components || []).forEach((component: any) => {
|
|
||||||
const type = component.type || "unknown";
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case "markers":
|
|
||||||
components.markers = {
|
|
||||||
showLabels: component.showlabel || false,
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "playermarkers":
|
|
||||||
components.playerMarkers = {
|
|
||||||
hideByDefault: component.hidebydefault || false,
|
|
||||||
layerName: component.label || "Players",
|
|
||||||
layerPriority: component.layerprio || 0,
|
|
||||||
showBodies: component.showplayerbody || false,
|
|
||||||
showSkinFaces: component.showplayerfaces || false,
|
|
||||||
showHealth: component.showplayerhealth || false,
|
|
||||||
smallFaces: component.smallplayerfaces || false,
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "coord":
|
|
||||||
components.coordinatesControl = {
|
|
||||||
showY: !(component.hidey || false),
|
|
||||||
label: component.label || "Location: ",
|
|
||||||
showRegion: component['show-mcr'] || false,
|
|
||||||
showChunk: component['show-chunk'] || false,
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "link":
|
|
||||||
components.linkControl = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "digitalclock":
|
|
||||||
components.clockControl = {
|
|
||||||
showDigitalClock: true,
|
|
||||||
showWeather: false,
|
|
||||||
showTimeOfDay: false,
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "timeofdayclock":
|
|
||||||
components.clockControl = {
|
|
||||||
showTimeOfDay: true,
|
|
||||||
showDigitalClock: component.showdigitalclock || false,
|
|
||||||
showWeather: component.showweather || false,
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "logo":
|
|
||||||
components.logoControls.push({
|
|
||||||
text: component.text || '',
|
|
||||||
url: component.linkurl || undefined,
|
|
||||||
position: component.position.replace('-', '') || 'topleft',
|
|
||||||
image: component.logourl || undefined,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "chat":
|
|
||||||
if (response.allowwebchat) {
|
|
||||||
components.chatSending = {
|
|
||||||
loginRequired: response['webchat-requires-login'] || false,
|
|
||||||
maxLength: response['chatlengthlimit'] || 256,
|
|
||||||
cooldown: response['webchat-interval'] || 5,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "chatbox":
|
|
||||||
components.chatBox = {
|
|
||||||
allowUrlName: component.allowurlname || false,
|
|
||||||
showPlayerFaces: component.showplayerfaces || false,
|
|
||||||
messageLifetime: component.messagettl || Infinity,
|
|
||||||
messageHistory: component.scrollback || Infinity,
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "chatballoon":
|
|
||||||
components.chatBalloons = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return components;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildMarkerSet(id: string, data: any): any {
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
label: data.label || "Unnamed set",
|
|
||||||
hidden: data.hide || false,
|
|
||||||
priority: data.layerprio || 0,
|
|
||||||
showLabels: data.showlabels || undefined,
|
|
||||||
minZoom: typeof data.minzoom !== 'undefined' && data.minzoom > -1 ? data.minzoom : undefined,
|
|
||||||
maxZoom: typeof data.maxzoom !== 'undefined' && data.maxzoom > -1 ? data.maxzoom : undefined,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildMarkers(data: any): Map<string, DynmapMarker> {
|
|
||||||
const markers = Object.freeze(new Map()) as Map<string, DynmapMarker>;
|
|
||||||
|
|
||||||
for (const key in data) {
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(data, key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
markers.set(key, buildMarker(data[key]));
|
|
||||||
}
|
|
||||||
|
|
||||||
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: typeof marker.minzoom !== 'undefined' && marker.minzoom > -1 ? marker.minzoom : undefined,
|
|
||||||
maxZoom: typeof marker.maxzoom !== 'undefined' && marker.maxzoom > -1 ? marker.maxzoom : undefined,
|
|
||||||
popupContent: marker.desc || undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildAreas(data: any): Map<string, DynmapArea> {
|
|
||||||
const areas = Object.freeze(new Map()) as Map<string, DynmapArea>;
|
|
||||||
|
|
||||||
for (const key in data) {
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(data, key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
areas.set(key, buildArea(data[key]));
|
|
||||||
}
|
|
||||||
|
|
||||||
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: typeof area.minzoom !== 'undefined' && area.minzoom > -1 ? area.minzoom : undefined,
|
|
||||||
maxZoom: typeof area.maxzoom !== 'undefined' && area.maxzoom > -1 ? area.maxzoom : undefined,
|
|
||||||
popupContent: area.desc || undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildLines(data: any): Map<string, DynmapLine> {
|
|
||||||
const lines = Object.freeze(new Map()) as Map<string, DynmapLine>;
|
|
||||||
|
|
||||||
for (const key in data) {
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(data, key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
lines.set(key, buildLine(data[key]));
|
|
||||||
}
|
|
||||||
|
|
||||||
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: typeof line.minzoom !== 'undefined' && line.minzoom > -1 ? line.minzoom : undefined,
|
|
||||||
maxZoom: typeof line.maxzoom !== 'undefined' && line.maxzoom > -1 ? line.maxzoom : undefined,
|
|
||||||
popupContent: line.desc || undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildCircles(data: any): Map<string, DynmapCircle> {
|
|
||||||
const circles = Object.freeze(new Map()) as Map<string, DynmapCircle>;
|
|
||||||
|
|
||||||
for (const key in data) {
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(data, key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
circles.set(key, buildCircle(data[key]));
|
|
||||||
}
|
|
||||||
|
|
||||||
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: typeof circle.minzoom !== 'undefined' && circle.minzoom > -1 ? circle.minzoom : undefined,
|
|
||||||
maxZoom: typeof circle.maxzoom !== 'undefined' && circle.maxzoom > -1 ? circle.maxzoom : undefined,
|
|
||||||
popupContent: circle.desc || undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildUpdates(data: Array<any>): DynmapUpdates {
|
|
||||||
const updates = {
|
|
||||||
markerSets: new Map<string, DynmapMarkerSetUpdates>(),
|
|
||||||
tiles: [] as DynmapTileUpdate[],
|
|
||||||
chat: [] as DynmapChat[],
|
|
||||||
},
|
|
||||||
dropped = {
|
|
||||||
stale: 0,
|
|
||||||
noSet: 0,
|
|
||||||
noId: 0,
|
|
||||||
unknownType: 0,
|
|
||||||
unknownCType: 0,
|
|
||||||
incomplete: 0,
|
|
||||||
notImplemented: 0,
|
|
||||||
},
|
|
||||||
lastUpdate = useStore().state.updateTimestamp;
|
|
||||||
|
|
||||||
let accepted = 0;
|
|
||||||
|
|
||||||
for (const entry of data) {
|
|
||||||
switch (entry.type) {
|
|
||||||
case 'component': {
|
|
||||||
if (lastUpdate && entry.timestamp < lastUpdate) {
|
|
||||||
dropped.stale++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!entry.id) {
|
|
||||||
dropped.noId++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Set updates don't have a set field, the id is the set
|
|
||||||
const set = entry.msg.startsWith("set") ? entry.id : entry.set;
|
|
||||||
|
|
||||||
if (!set) {
|
|
||||||
dropped.noSet++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.ctype !== 'markers') {
|
|
||||||
dropped.unknownCType++;
|
|
||||||
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'),
|
|
||||||
};
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
} 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
accepted++;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'chat':
|
|
||||||
if (!entry.message || !entry.timestamp) {
|
|
||||||
dropped.incomplete++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.timestamp < lastUpdate) {
|
|
||||||
dropped.stale++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.source !== 'player' && entry.source !== 'web') {
|
|
||||||
dropped.notImplemented++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
updates.chat.push({
|
|
||||||
type: 'chat',
|
|
||||||
source: entry.source || undefined,
|
|
||||||
playerAccount: entry.account || undefined,
|
|
||||||
playerName: entry.playerName || undefined,
|
|
||||||
message: entry.message || "",
|
|
||||||
timestamp: entry.timestamp,
|
|
||||||
channel: entry.channel || undefined,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'playerjoin':
|
|
||||||
if (!entry.account || !entry.timestamp) {
|
|
||||||
dropped.incomplete++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.timestamp < lastUpdate) {
|
|
||||||
dropped.stale++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
updates.chat.push({
|
|
||||||
type: 'playerjoin',
|
|
||||||
playerAccount: entry.account,
|
|
||||||
playerName: entry.playerName || "",
|
|
||||||
timestamp: entry.timestamp || undefined,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'playerquit':
|
|
||||||
if (!entry.account || !entry.timestamp) {
|
|
||||||
dropped.incomplete++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.timestamp < lastUpdate) {
|
|
||||||
dropped.stale++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
updates.chat.push({
|
|
||||||
type: 'playerleave',
|
|
||||||
playerAccount: entry.account,
|
|
||||||
playerName: entry.playerName || "",
|
|
||||||
timestamp: entry.timestamp || undefined,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'tile':
|
|
||||||
if (!entry.name || !entry.timestamp) {
|
|
||||||
dropped.incomplete++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastUpdate && entry.timestamp < lastUpdate) {
|
|
||||||
dropped.stale++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
updates.tiles.push({
|
|
||||||
name: entry.name,
|
|
||||||
timestamp: entry.timestamp,
|
|
||||||
});
|
|
||||||
|
|
||||||
accepted++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
dropped.unknownType++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Sort chat by newest first
|
|
||||||
updates.chat = updates.chat.sort((one, two) => {
|
|
||||||
return two.timestamp - one.timestamp;
|
|
||||||
});
|
|
||||||
|
|
||||||
console.debug(`Updates: ${accepted} accepted. Rejected: `, dropped);
|
|
||||||
|
|
||||||
return updates;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchJSON(url: string, signal: AbortSignal) {
|
|
||||||
let response, json;
|
|
||||||
|
|
||||||
try {
|
|
||||||
response = await fetch(url, {signal});
|
|
||||||
} catch(e) {
|
|
||||||
if(e instanceof DOMException && e.name === 'AbortError') {
|
|
||||||
console.warn(`Request aborted (${url}`);
|
|
||||||
throw e;
|
|
||||||
} else {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`Network request failed`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Network request failed (${response.statusText || 'Unknown'})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
json = await response.json();
|
|
||||||
} catch(e) {
|
|
||||||
if(e instanceof DOMException && e.name === 'AbortError') {
|
|
||||||
console.warn(`Request aborted (${url}`);
|
|
||||||
throw e;
|
|
||||||
} else {
|
|
||||||
throw new Error('Request returned invalid json');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
let configurationAbort: AbortController | undefined = undefined,
|
|
||||||
markersAbort: AbortController | undefined = undefined,
|
|
||||||
updateAbort: AbortController | undefined = undefined;
|
|
||||||
|
|
||||||
export default {
|
|
||||||
async getConfiguration(): Promise<DynmapConfigurationResponse> {
|
|
||||||
if(configurationAbort) {
|
|
||||||
configurationAbort.abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
configurationAbort = new AbortController();
|
|
||||||
|
|
||||||
const response = await fetchJSON(useStore().getters.serverConfig.dynmap.configuration, configurationAbort.signal);
|
|
||||||
|
|
||||||
if (response.error === 'login-required') {
|
|
||||||
throw new Error("Login required");
|
|
||||||
} else if (response.error) {
|
|
||||||
throw new Error(response.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
config: buildServerConfig(response),
|
|
||||||
messages: buildMessagesConfig(response),
|
|
||||||
worlds: buildWorlds(response),
|
|
||||||
components: buildComponents(response),
|
|
||||||
loggedIn: response.loggedin || false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async getUpdate(requestId: number, world: string, timestamp: number): Promise<DynmapUpdateResponse> {
|
|
||||||
let url = useStore().getters.serverConfig.dynmap.update;
|
|
||||||
url = url.replace('{world}', world);
|
|
||||||
url = url.replace('{timestamp}', timestamp.toString());
|
|
||||||
|
|
||||||
if(updateAbort) {
|
|
||||||
updateAbort.abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateAbort = new AbortController();
|
|
||||||
|
|
||||||
const response = await fetchJSON(url, updateAbort.signal);
|
|
||||||
const players: Set<DynmapPlayer> = new Set();
|
|
||||||
|
|
||||||
(response.players || []).forEach((player: any) => {
|
|
||||||
const world = player.world && player.world !== '-some-other-bogus-world-' ? player.world : undefined;
|
|
||||||
|
|
||||||
players.add({
|
|
||||||
account: player.account || "",
|
|
||||||
health: player.health || 0,
|
|
||||||
armor: player.armor || 0,
|
|
||||||
name: player.name || "",
|
|
||||||
sort: player.sort || 0,
|
|
||||||
hidden: !world,
|
|
||||||
location: {
|
|
||||||
//Add 0.5 to position in the middle of a block
|
|
||||||
x: !isNaN(player.x) ? player.x + 0.5 : 0,
|
|
||||||
y: !isNaN(player.y) ? player.y : 0,
|
|
||||||
z: !isNaN(player.z) ? player.z + 0.5 : 0,
|
|
||||||
world: world,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//Extra fake players for testing
|
|
||||||
// for(let i = 0; i < 450; i++) {
|
|
||||||
// players.add({
|
|
||||||
// account: "VIDEO GAMES " + i,
|
|
||||||
// health: Math.round(Math.random() * 10),
|
|
||||||
// armor: Math.round(Math.random() * 10),
|
|
||||||
// name: "VIDEO GAMES " + i,
|
|
||||||
// sort: Math.round(Math.random() * 10),
|
|
||||||
// hidden: false,
|
|
||||||
// location: {
|
|
||||||
// x: Math.round(Math.random() * 1000) - 500,
|
|
||||||
// y: 64,
|
|
||||||
// z: Math.round(Math.random() * 1000) - 500,
|
|
||||||
// world: "world",
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
return {
|
|
||||||
worldState: {
|
|
||||||
timeOfDay: response.servertime || 0,
|
|
||||||
thundering: response.isThundering || false,
|
|
||||||
raining: response.hasStorm || false,
|
|
||||||
},
|
|
||||||
playerCount: response.count || 0,
|
|
||||||
configHash: response.confighash || 0,
|
|
||||||
timestamp: response.timestamp || 0,
|
|
||||||
players,
|
|
||||||
updates: buildUpdates(response.updates || []),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async getMarkerSets(world: string): Promise<Map<string, DynmapMarkerSet>> {
|
|
||||||
const url = `${useStore().getters.serverConfig.dynmap.markers}_markers_/marker_${world}.json`;
|
|
||||||
|
|
||||||
if(markersAbort) {
|
|
||||||
markersAbort.abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
markersAbort = new AbortController();
|
|
||||||
|
|
||||||
const response = await fetchJSON(url, markersAbort.signal);
|
|
||||||
const sets: Map<string, DynmapMarkerSet> = new Map();
|
|
||||||
|
|
||||||
response.sets = response.sets || {};
|
|
||||||
|
|
||||||
for (const key in response.sets) {
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(response.sets, key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const set = response.sets[key],
|
|
||||||
markers = buildMarkers(set.markers || {}),
|
|
||||||
circles = buildCircles(set.circles || {}),
|
|
||||||
areas = buildAreas(set.areas || {}),
|
|
||||||
lines = buildLines(set.lines || {});
|
|
||||||
|
|
||||||
sets.set(key, {
|
|
||||||
...buildMarkerSet(key, set),
|
|
||||||
markers,
|
|
||||||
circles,
|
|
||||||
areas,
|
|
||||||
lines,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return sets;
|
|
||||||
},
|
|
||||||
|
|
||||||
sendChatMessage(message: string) {
|
|
||||||
const store = useStore();
|
|
||||||
|
|
||||||
if (!store.state.components.chatSending) {
|
|
||||||
return Promise.reject(store.state.messages.chatErrorDisabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fetch(useStore().getters.serverConfig.dynmap.sendmessage, {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify({
|
|
||||||
name: null,
|
|
||||||
message: message,
|
|
||||||
})
|
|
||||||
}).then((response) => {
|
|
||||||
if (response.status === 403) { //Rate limited
|
|
||||||
throw new ChatError(store.state.messages.chatErrorCooldown
|
|
||||||
.replace('%interval%', store.state.components.chatSending!.cooldown.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error('Network request failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.json();
|
|
||||||
}).then(response => {
|
|
||||||
if (response.error !== 'none') {
|
|
||||||
throw new ChatError(store.state.messages.chatErrorNotAllowed);
|
|
||||||
}
|
|
||||||
}).catch(e => {
|
|
||||||
if (!(e instanceof ChatError)) {
|
|
||||||
console.error(store.state.messages.chatErrorUnknown);
|
|
||||||
console.trace(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw e;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -210,4 +210,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -44,12 +44,10 @@ import LinkControl from "@/components/map/control/LinkControl.vue";
|
|||||||
import ChatControl from "@/components/map/control/ChatControl.vue";
|
import ChatControl from "@/components/map/control/ChatControl.vue";
|
||||||
import LogoControl from "@/components/map/control/LogoControl.vue";
|
import LogoControl from "@/components/map/control/LogoControl.vue";
|
||||||
import {MutationTypes} from "@/store/mutation-types";
|
import {MutationTypes} from "@/store/mutation-types";
|
||||||
import {DynmapPlayer} from "@/dynmap";
|
|
||||||
import {ActionTypes} from "@/store/action-types";
|
|
||||||
import LiveAtlasLeafletMap from "@/leaflet/LiveAtlasLeafletMap";
|
import LiveAtlasLeafletMap from "@/leaflet/LiveAtlasLeafletMap";
|
||||||
import {LoadingControl} from "@/leaflet/control/LoadingControl";
|
import {LoadingControl} from "@/leaflet/control/LoadingControl";
|
||||||
import MapContextMenu from "@/components/map/MapContextMenu.vue";
|
import MapContextMenu from "@/components/map/MapContextMenu.vue";
|
||||||
import {Coordinate} from "@/index";
|
import {Coordinate, LiveAtlasPlayer} from "@/index";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@ -125,7 +123,7 @@ export default defineComponent({
|
|||||||
followTarget: {
|
followTarget: {
|
||||||
handler(newValue, oldValue) {
|
handler(newValue, oldValue) {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
this.updateFollow(newValue, !oldValue || newValue.account !== oldValue.account);
|
this.updateFollow(newValue, !oldValue || newValue.name !== oldValue.name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deep: true
|
deep: true
|
||||||
@ -141,8 +139,14 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
currentMap(newValue, oldValue) {
|
currentMap(newValue, oldValue) {
|
||||||
if(this.leaflet && newValue && oldValue) {
|
if(this.leaflet && newValue) {
|
||||||
const panTarget = this.scheduledPan || oldValue.latLngToLocation(this.leaflet.getCenter(), 64);
|
let panTarget = this.scheduledPan;
|
||||||
|
|
||||||
|
if(!panTarget && oldValue) {
|
||||||
|
panTarget = oldValue.latLngToLocation(this.leaflet.getCenter(), 64);
|
||||||
|
} else if(!panTarget) {
|
||||||
|
panTarget = {x: 0, y: 0, z: 0};
|
||||||
|
}
|
||||||
|
|
||||||
if(this.scheduledZoom) {
|
if(this.scheduledZoom) {
|
||||||
this.leaflet!.setZoom(this.scheduledZoom, {
|
this.leaflet!.setZoom(this.scheduledZoom, {
|
||||||
@ -165,8 +169,6 @@ export default defineComponent({
|
|||||||
if(newValue) {
|
if(newValue) {
|
||||||
let location: Coordinate | null = this.scheduledPan;
|
let location: Coordinate | null = this.scheduledPan;
|
||||||
|
|
||||||
store.dispatch(ActionTypes.GET_MARKER_SETS, undefined);
|
|
||||||
|
|
||||||
// Abort if follow target is present, to avoid panning twice
|
// Abort if follow target is present, to avoid panning twice
|
||||||
if(store.state.followTarget && store.state.followTarget.location.world === newValue.name) {
|
if(store.state.followTarget && store.state.followTarget.location.world === newValue.name) {
|
||||||
return;
|
return;
|
||||||
@ -280,7 +282,7 @@ export default defineComponent({
|
|||||||
this.leaflet.getContainer().focus();
|
this.leaflet.getContainer().focus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateFollow(player: DynmapPlayer, newFollow: boolean) {
|
updateFollow(player: LiveAtlasPlayer, newFollow: boolean) {
|
||||||
const store = useStore(),
|
const store = useStore(),
|
||||||
followMapName = store.state.configuration.followMap,
|
followMapName = store.state.configuration.followMap,
|
||||||
currentWorld = store.state.currentWorld;
|
currentWorld = store.state.currentWorld;
|
||||||
@ -288,17 +290,17 @@ export default defineComponent({
|
|||||||
let targetWorld = null;
|
let targetWorld = null;
|
||||||
|
|
||||||
if(!this.leaflet) {
|
if(!this.leaflet) {
|
||||||
console.warn(`Cannot follow ${player.account}. Map not yet initialized.`);
|
console.warn(`Cannot follow ${player.name}. Map not yet initialized.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(player.hidden) {
|
if(player.hidden) {
|
||||||
console.warn(`Cannot follow ${player.account}. Player is hidden from the map.`);
|
console.warn(`Cannot follow ${player.name}. Player is hidden from the map.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!player.location.world) {
|
if(!player.location.world) {
|
||||||
console.warn(`Cannot follow ${player.account}. Player isn't in a known world.`);
|
console.warn(`Cannot follow ${player.name}. Player isn't in a known world.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,7 +311,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!targetWorld) {
|
if (!targetWorld) {
|
||||||
console.warn(`Cannot follow ${player.account}. Player isn't in a known world.`);
|
console.warn(`Cannot follow ${player.name}. Player isn't in a known world.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,7 +322,7 @@ export default defineComponent({
|
|||||||
if(map !== store.state.currentMap && (targetWorld !== currentWorld || newFollow)) {
|
if(map !== store.state.currentMap && (targetWorld !== currentWorld || newFollow)) {
|
||||||
this.scheduledPan = player.location;
|
this.scheduledPan = player.location;
|
||||||
|
|
||||||
if(newFollow) {
|
if(newFollow && store.state.configuration.followZoom) {
|
||||||
console.log(`Setting zoom for new follow ${store.state.configuration.followZoom}`);
|
console.log(`Setting zoom for new follow ${store.state.configuration.followZoom}`);
|
||||||
this.scheduledZoom = store.state.configuration.followZoom;
|
this.scheduledZoom = store.state.configuration.followZoom;
|
||||||
}
|
}
|
||||||
@ -330,7 +332,7 @@ export default defineComponent({
|
|||||||
} else {
|
} else {
|
||||||
this.leaflet!.panTo(store.state.currentMap?.locationToLatLng(player.location));
|
this.leaflet!.panTo(store.state.currentMap?.locationToLatLng(player.location));
|
||||||
|
|
||||||
if(newFollow) {
|
if(newFollow && store.state.configuration.followZoom) {
|
||||||
console.log(`Setting zoom for new follow ${store.state.configuration.followZoom}`);
|
console.log(`Setting zoom for new follow ${store.state.configuration.followZoom}`);
|
||||||
this.leaflet!.setZoom(store.state.configuration.followZoom);
|
this.leaflet!.setZoom(store.state.configuration.followZoom);
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,25 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="sidebar" role="none" ref="sidebar">
|
<section class="sidebar" role="none" ref="sidebar">
|
||||||
<header class="sidebar__buttons">
|
<header class="sidebar__buttons">
|
||||||
<button v-if="mapCount > 1" :class="{'button--maps': true}" @click="toggleMaps" :title="messageWorlds"
|
<button v-if="mapCount > 1 || serverCount > 1" :class="{'button--maps': true}" @click="toggleMaps"
|
||||||
:aria-label="messageWorlds" :aria-expanded="mapsVisible" @keydown="handleMapsKeydown">
|
:title="messageWorlds" :aria-label="messageWorlds" :aria-expanded="mapsVisible"
|
||||||
|
@keydown="handleMapsKeydown">
|
||||||
<SvgIcon name="maps"></SvgIcon>
|
<SvgIcon name="maps"></SvgIcon>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="playerMakersEnabled" :class="{'button--players': true}" @click="togglePlayers"
|
<button v-if="playerMakersEnabled" :class="{'button--players': true}" @click="togglePlayers"
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -24,15 +24,15 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, ref, onMounted, computed} from "@vue/runtime-core";
|
import {defineComponent, ref, onMounted, computed} from "@vue/runtime-core";
|
||||||
import {DynmapChat} from "@/dynmap";
|
|
||||||
import {getMinecraftHead} from '@/util';
|
import {getMinecraftHead} from '@/util';
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import defaultImage from '@/assets/images/player_face.png';
|
import defaultImage from '@/assets/images/player_face.png';
|
||||||
|
import {LiveAtlasChat} from "@/index";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
message: {
|
message: {
|
||||||
type: Object as () => DynmapChat,
|
type: Object as () => LiveAtlasChat,
|
||||||
required: true,
|
required: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
<!--
|
||||||
|
- Copyright 2021 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.
|
||||||
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<nav role="none" id="map-context-menu" ref="menuElement" :style="style" @keydown="handleKeydown">
|
<nav role="none" id="map-context-menu" ref="menuElement" :style="style" @keydown="handleKeydown">
|
||||||
<ul class="menu" role="menu">
|
<ul class="menu" role="menu">
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, onUnmounted, computed, watch} from "@vue/runtime-core";
|
import {defineComponent, onUnmounted, computed, watch} from "@vue/runtime-core";
|
||||||
import {Map} from 'leaflet';
|
import {Map} from 'leaflet';
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import {ActionTypes} from "@/store/action-types";
|
|
||||||
import {getMinecraftTime} from "@/util";
|
|
||||||
import {DynmapTileLayer} from "@/leaflet/tileLayer/DynmapTileLayer";
|
import {DynmapTileLayer} from "@/leaflet/tileLayer/DynmapTileLayer";
|
||||||
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
||||||
|
import {LiveAtlasTileLayer} from "@/leaflet/tileLayer/LiveAtlasTileLayer";
|
||||||
|
import {Pl3xmapTileLayer} from "@/leaflet/tileLayer/Pl3xmapTileLayer";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@ -40,58 +40,32 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
setup(props) {
|
setup(props) {
|
||||||
let updateFrame = 0,
|
|
||||||
stopUpdateWatch: Function;
|
|
||||||
|
|
||||||
const store = useStore(),
|
const store = useStore(),
|
||||||
night = computed(() => getMinecraftTime(store.state.currentWorldState.timeOfDay).night),
|
active = computed(() => props.map === store.state.currentMap);
|
||||||
|
|
||||||
|
let layer: LiveAtlasTileLayer;
|
||||||
|
|
||||||
|
if(store.state.currentServer?.type === 'dynmap') {
|
||||||
layer = new DynmapTileLayer({
|
layer = new DynmapTileLayer({
|
||||||
errorTileUrl: 'images/blank.png',
|
errorTileUrl: 'images/blank.png',
|
||||||
mapSettings: Object.freeze(JSON.parse(JSON.stringify(props.map))),
|
mapSettings: Object.freeze(JSON.parse(JSON.stringify(props.map))),
|
||||||
night: night.value,
|
});
|
||||||
}),
|
} else {
|
||||||
pendingUpdates = computed(() => !!store.state.pendingTileUpdates.length),
|
layer = new Pl3xmapTileLayer({
|
||||||
active = computed(() => props.map === store.state.currentMap),
|
errorTileUrl: 'images/blank.png',
|
||||||
|
mapSettings: Object.freeze(JSON.parse(JSON.stringify(props.map)))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
enableLayer = () => {
|
const enableLayer = () => {
|
||||||
props.leaflet.addLayer(layer);
|
props.leaflet.addLayer(layer);
|
||||||
|
|
||||||
stopUpdateWatch = watch(pendingUpdates, (newValue, oldValue) => {
|
|
||||||
if(newValue && !oldValue && !updateFrame) {
|
|
||||||
handlePendingUpdates();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
disableLayer = () => {
|
disableLayer = () => {
|
||||||
layer.remove();
|
layer.remove();
|
||||||
|
|
||||||
if(stopUpdateWatch) {
|
|
||||||
stopUpdateWatch();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handlePendingUpdates = async () => {
|
|
||||||
const updates = await useStore().dispatch(ActionTypes.POP_TILE_UPDATES, 10);
|
|
||||||
|
|
||||||
for(const update of updates) {
|
|
||||||
layer.updateNamedTile(update.name, update.timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(pendingUpdates.value) {
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
|
|
||||||
} else {
|
|
||||||
updateFrame = 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(active, (newValue) => newValue ? enableLayer() : disableLayer());
|
watch(active, (newValue) => newValue ? enableLayer() : disableLayer());
|
||||||
watch(night, (newValue) => {
|
|
||||||
if(props.map.nightAndDay) {
|
|
||||||
layer.setNight(newValue);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(active.value) {
|
if(active.value) {
|
||||||
enableLayer();
|
enableLayer();
|
||||||
@ -99,10 +73,6 @@ export default defineComponent({
|
|||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
disableLayer();
|
disableLayer();
|
||||||
|
|
||||||
if(updateFrame) {
|
|
||||||
cancelAnimationFrame(updateFrame);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -24,13 +24,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, computed} from "@vue/runtime-core";
|
import {defineComponent, computed} from "@vue/runtime-core";
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import {DynmapMarkerSet} from "@/dynmap";
|
|
||||||
import Areas from "@/components/map/vector/Areas.vue";
|
import Areas from "@/components/map/vector/Areas.vue";
|
||||||
import Circles from "@/components/map/vector/Circles.vue";
|
import Circles from "@/components/map/vector/Circles.vue";
|
||||||
import Lines from "@/components/map/vector/Lines.vue";
|
import Lines from "@/components/map/vector/Lines.vue";
|
||||||
import Markers from "@/components/map/vector/Markers.vue";
|
import Markers from "@/components/map/vector/Markers.vue";
|
||||||
import LiveAtlasLeafletMap from "@/leaflet/LiveAtlasLeafletMap";
|
import LiveAtlasLeafletMap from "@/leaflet/LiveAtlasLeafletMap";
|
||||||
import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup";
|
import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup";
|
||||||
|
import {LiveAtlasMarkerSet} from "@/index";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@ -47,7 +47,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
markerSet: {
|
markerSet: {
|
||||||
type: Object as () => DynmapMarkerSet,
|
type: Object as () => LiveAtlasMarkerSet,
|
||||||
required: true,
|
required: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -62,22 +62,24 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
const store = useStore();
|
||||||
|
|
||||||
if(!this.componentSettings!.hideByDefault) {
|
if(!this.componentSettings!.hideByDefault) {
|
||||||
this.leaflet.getLayerManager().addLayer(
|
this.leaflet.getLayerManager().addLayer(
|
||||||
this.layerGroup,
|
this.layerGroup,
|
||||||
true,
|
true,
|
||||||
useStore().state.messages.playersHeading,
|
store.state.components.playerMarkers!.layerName,
|
||||||
this.componentSettings!.layerPriority);
|
this.componentSettings!.layerPriority);
|
||||||
} else {
|
} else {
|
||||||
this.leaflet.getLayerManager().addHiddenLayer(
|
this.leaflet.getLayerManager().addHiddenLayer(
|
||||||
this.layerGroup,
|
this.layerGroup,
|
||||||
useStore().state.messages.playersHeading,
|
store.state.components.playerMarkers!.layerName,
|
||||||
this.componentSettings!.layerPriority);
|
this.componentSettings!.layerPriority);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
unmounted() {
|
unmounted() {
|
||||||
this.leaflet.removeLayer(this.layerGroup);
|
this.leaflet.getLayerManager().removeLayer(this.layerGroup);
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, computed, ref, onMounted, onUnmounted} from "@vue/runtime-core";
|
import {defineComponent, computed, ref, onMounted, onUnmounted} from "@vue/runtime-core";
|
||||||
import {LayerGroup} from 'leaflet';
|
import {LayerGroup} from 'leaflet';
|
||||||
import {DynmapChat, DynmapPlayer} from "@/dynmap";
|
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import {PlayerMarker} from "@/leaflet/marker/PlayerMarker";
|
import {PlayerMarker} from "@/leaflet/marker/PlayerMarker";
|
||||||
import {Popup} from "leaflet";
|
import {Popup} from "leaflet";
|
||||||
|
import {LiveAtlasChat, LiveAtlasPlayer} from "@/index";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
player: {
|
player: {
|
||||||
type: Object as () => DynmapPlayer,
|
type: Object as () => LiveAtlasPlayer,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
layerGroup: {
|
layerGroup: {
|
||||||
@ -79,7 +79,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
//Chat messages to show in the popup
|
//Chat messages to show in the popup
|
||||||
playerChat = computed(() => {
|
playerChat = computed(() => {
|
||||||
const messages: DynmapChat[] = [];
|
const messages: LiveAtlasChat[] = [];
|
||||||
|
|
||||||
if(!chatBalloonsEnabled.value) {
|
if(!chatBalloonsEnabled.value) {
|
||||||
return messages;
|
return messages;
|
||||||
@ -96,7 +96,7 @@ export default defineComponent({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(message.type === 'chat' && message.playerAccount === props.player.account) {
|
if(message.type === 'chat' && message.playerAccount === props.player.name) {
|
||||||
messages.push(message);
|
messages.push(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,7 +218,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
playerChat(newValue: DynmapChat[]) {
|
playerChat(newValue: LiveAtlasChat[]) {
|
||||||
if(!this.chatBalloonsEnabled || !this.markerVisible || !newValue.length) {
|
if(!this.chatBalloonsEnabled || !this.markerVisible || !newValue.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,33 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
|
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import {DynmapArea, DynmapMarkerSet} from "@/dynmap";
|
|
||||||
import {ActionTypes} from "@/store/action-types";
|
import {ActionTypes} from "@/store/action-types";
|
||||||
import {createArea, updateArea} from "@/util/areas";
|
import {createArea, updateArea} from "@/util/areas";
|
||||||
import {getPointConverter} from '@/util';
|
|
||||||
import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup";
|
import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup";
|
||||||
import LiveAtlasPolygon from "@/leaflet/vector/LiveAtlasPolygon";
|
import LiveAtlasPolygon from "@/leaflet/vector/LiveAtlasPolygon";
|
||||||
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
|
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
|
||||||
|
import {LiveAtlasArea, LiveAtlasMarkerSet} from "@/index";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
set: {
|
set: {
|
||||||
type: Object as () => DynmapMarkerSet,
|
type: Object as () => LiveAtlasMarkerSet,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
layerGroup: {
|
layerGroup: {
|
||||||
@ -50,9 +49,9 @@ export default defineComponent({
|
|||||||
layers = Object.freeze(new Map()) as Map<string, LiveAtlasPolygon | LiveAtlasPolyline>,
|
layers = Object.freeze(new Map()) as Map<string, LiveAtlasPolygon | LiveAtlasPolyline>,
|
||||||
|
|
||||||
createAreas = () => {
|
createAreas = () => {
|
||||||
const converter = getPointConverter();
|
const converter = currentMap.value!.locationToLatLng.bind(currentMap.value);
|
||||||
|
|
||||||
props.set.areas.forEach((area: DynmapArea, id: string) => {
|
props.set.areas.forEach((area: LiveAtlasArea, id: string) => {
|
||||||
const layer = createArea(area, converter);
|
const layer = createArea(area, converter);
|
||||||
|
|
||||||
layers.set(id, layer);
|
layers.set(id, layer);
|
||||||
@ -72,18 +71,17 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handlePendingUpdates = async () => {
|
handlePendingUpdates = async () => {
|
||||||
const updates = await useStore().dispatch(ActionTypes.POP_AREA_UPDATES, {
|
const updates = await store.dispatch(ActionTypes.POP_AREA_UPDATES, {
|
||||||
markerSet: props.set.id,
|
markerSet: props.set.id,
|
||||||
amount: 10,
|
amount: 10,
|
||||||
});
|
}),
|
||||||
|
converter = currentMap.value!.locationToLatLng.bind(currentMap.value);
|
||||||
const converter = getPointConverter();
|
|
||||||
|
|
||||||
for(const update of updates) {
|
for(const update of updates) {
|
||||||
if(update.removed) {
|
if(update.removed) {
|
||||||
deleteArea(update.id);
|
deleteArea(update.id);
|
||||||
} else {
|
} else {
|
||||||
const layer = updateArea(layers.get(update.id), update.payload as DynmapArea, converter)
|
const layer = updateArea(layers.get(update.id), update.payload as LiveAtlasArea, converter);
|
||||||
|
|
||||||
if(!layers.has(update.id)) {
|
if(!layers.has(update.id)) {
|
||||||
props.layerGroup.addLayer(layer);
|
props.layerGroup.addLayer(layer);
|
||||||
@ -101,10 +99,9 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//FIXME: Prevent unnecessary repositioning when changing worlds
|
watch(currentMap, (newValue, oldValue) => {
|
||||||
watch(currentMap, (newValue) => {
|
if(newValue && (!oldValue || oldValue.world === newValue.world)) {
|
||||||
if(newValue) {
|
const converter = newValue.locationToLatLng.bind(newValue);
|
||||||
const converter = getPointConverter();
|
|
||||||
|
|
||||||
for (const [id, area] of props.set.areas) {
|
for (const [id, area] of props.set.areas) {
|
||||||
updateArea(layers.get(id), area, converter);
|
updateArea(layers.get(id), area, converter);
|
||||||
|
@ -1,34 +1,33 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
|
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import {DynmapCircle, DynmapMarkerSet} from "@/dynmap";
|
|
||||||
import {ActionTypes} from "@/store/action-types";
|
import {ActionTypes} from "@/store/action-types";
|
||||||
import {createCircle, updateCircle} from "@/util/circles";
|
import {createCircle, updateCircle} from "@/util/circles";
|
||||||
import {getPointConverter} from '@/util';
|
|
||||||
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
|
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
|
||||||
import LiveAtlasPolygon from "@/leaflet/vector/LiveAtlasPolygon";
|
import LiveAtlasPolygon from "@/leaflet/vector/LiveAtlasPolygon";
|
||||||
import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup";
|
import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup";
|
||||||
|
import {LiveAtlasCircle, LiveAtlasMarkerSet} from "@/index";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
set: {
|
set: {
|
||||||
type: Object as () => DynmapMarkerSet,
|
type: Object as () => LiveAtlasMarkerSet,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
layerGroup: {
|
layerGroup: {
|
||||||
@ -50,9 +49,9 @@ export default defineComponent({
|
|||||||
layers = Object.freeze(new Map<string, LiveAtlasPolyline | LiveAtlasPolygon>()),
|
layers = Object.freeze(new Map<string, LiveAtlasPolyline | LiveAtlasPolygon>()),
|
||||||
|
|
||||||
createCircles = () => {
|
createCircles = () => {
|
||||||
const converter = getPointConverter();
|
const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
|
||||||
|
|
||||||
props.set.circles.forEach((circle: DynmapCircle, id: string) => {
|
props.set.circles.forEach((circle: LiveAtlasCircle, id: string) => {
|
||||||
const layer = createCircle(circle, converter);
|
const layer = createCircle(circle, converter);
|
||||||
|
|
||||||
layers.set(id, layer);
|
layers.set(id, layer);
|
||||||
@ -75,15 +74,14 @@ export default defineComponent({
|
|||||||
const updates = await useStore().dispatch(ActionTypes.POP_CIRCLE_UPDATES, {
|
const updates = await useStore().dispatch(ActionTypes.POP_CIRCLE_UPDATES, {
|
||||||
markerSet: props.set.id,
|
markerSet: props.set.id,
|
||||||
amount: 10,
|
amount: 10,
|
||||||
});
|
}),
|
||||||
|
converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
|
||||||
const converter = getPointConverter();
|
|
||||||
|
|
||||||
for(const update of updates) {
|
for(const update of updates) {
|
||||||
if(update.removed) {
|
if(update.removed) {
|
||||||
deleteCircle(update.id);
|
deleteCircle(update.id);
|
||||||
} else {
|
} else {
|
||||||
const layer = updateCircle(layers.get(update.id), update.payload as DynmapCircle, converter)
|
const layer = updateCircle(layers.get(update.id), update.payload as LiveAtlasCircle, converter)
|
||||||
|
|
||||||
if(!layers.has(update.id)) {
|
if(!layers.has(update.id)) {
|
||||||
props.layerGroup.addLayer(layer);
|
props.layerGroup.addLayer(layer);
|
||||||
@ -101,10 +99,9 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//FIXME: Prevent unnecessary repositioning when changing worlds
|
watch(currentMap, (newValue, oldValue) => {
|
||||||
watch(currentMap, (newValue) => {
|
if(newValue && (!oldValue || oldValue.world === newValue.world)) {
|
||||||
if(newValue) {
|
const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
|
||||||
const converter = getPointConverter();
|
|
||||||
|
|
||||||
for (const [id, circle] of props.set.circles) {
|
for (const [id, circle] of props.set.circles) {
|
||||||
updateCircle(layers.get(id), circle, converter);
|
updateCircle(layers.get(id), circle, converter);
|
||||||
|
@ -1,33 +1,32 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
|
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import {DynmapLine, DynmapMarkerSet} from "@/dynmap";
|
|
||||||
import {ActionTypes} from "@/store/action-types";
|
import {ActionTypes} from "@/store/action-types";
|
||||||
import {createLine, updateLine} from "@/util/lines";
|
import {createLine, updateLine} from "@/util/lines";
|
||||||
import {getPointConverter} from '@/util';
|
|
||||||
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
|
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
|
||||||
import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup";
|
import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup";
|
||||||
|
import {LiveAtlasLine, LiveAtlasMarkerSet} from "@/index";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
set: {
|
set: {
|
||||||
type: Object as () => DynmapMarkerSet,
|
type: Object as () => LiveAtlasMarkerSet,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
layerGroup: {
|
layerGroup: {
|
||||||
@ -49,9 +48,9 @@ export default defineComponent({
|
|||||||
layers = Object.freeze(new Map<string, LiveAtlasPolyline>()),
|
layers = Object.freeze(new Map<string, LiveAtlasPolyline>()),
|
||||||
|
|
||||||
createLines = () => {
|
createLines = () => {
|
||||||
const converter = getPointConverter();
|
const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
|
||||||
|
|
||||||
props.set.lines.forEach((line: DynmapLine, id: string) => {
|
props.set.lines.forEach((line: LiveAtlasLine, id: string) => {
|
||||||
const layer = createLine(line, converter);
|
const layer = createLine(line, converter);
|
||||||
|
|
||||||
layers.set(id, layer);
|
layers.set(id, layer);
|
||||||
@ -74,15 +73,14 @@ export default defineComponent({
|
|||||||
const updates = await useStore().dispatch(ActionTypes.POP_LINE_UPDATES, {
|
const updates = await useStore().dispatch(ActionTypes.POP_LINE_UPDATES, {
|
||||||
markerSet: props.set.id,
|
markerSet: props.set.id,
|
||||||
amount: 10,
|
amount: 10,
|
||||||
});
|
}),
|
||||||
|
converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
|
||||||
const converter = getPointConverter();
|
|
||||||
|
|
||||||
for(const update of updates) {
|
for(const update of updates) {
|
||||||
if(update.removed) {
|
if(update.removed) {
|
||||||
deleteLine(update.id);
|
deleteLine(update.id);
|
||||||
} else {
|
} else {
|
||||||
const layer = updateLine(layers.get(update.id), update.payload as DynmapLine, converter)
|
const layer = updateLine(layers.get(update.id), update.payload as LiveAtlasLine, converter)
|
||||||
|
|
||||||
if(!layers.has(update.id)) {
|
if(!layers.has(update.id)) {
|
||||||
props.layerGroup.addLayer(layer);
|
props.layerGroup.addLayer(layer);
|
||||||
@ -100,10 +98,9 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//FIXME: Prevent unnecessary repositioning when changing worlds
|
watch(currentMap, (newValue, oldValue) => {
|
||||||
watch(currentMap, (newValue) => {
|
if(newValue && (!oldValue || oldValue.world === newValue.world)) {
|
||||||
if(newValue) {
|
const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
|
||||||
const converter = getPointConverter();
|
|
||||||
|
|
||||||
for (const [id, line] of props.set.lines) {
|
for (const [id, line] of props.set.lines) {
|
||||||
updateLine(layers.get(id), line, converter);
|
updateLine(layers.get(id), line, converter);
|
||||||
|
@ -1,33 +1,32 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
|
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
|
||||||
import {Marker} from 'leaflet';
|
import {Marker} from 'leaflet';
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import {DynmapMarker, DynmapMarkerSet} from "@/dynmap";
|
|
||||||
import {ActionTypes} from "@/store/action-types";
|
import {ActionTypes} from "@/store/action-types";
|
||||||
import {createMarker, updateMarker} from "@/util/markers";
|
import {createMarker, updateMarker} from "@/util/markers";
|
||||||
import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup";
|
import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup";
|
||||||
import {getPointConverter} from "@/util";
|
import {LiveAtlasMarker, LiveAtlasMarkerSet} from "@/index";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
set: {
|
set: {
|
||||||
type: Object as () => DynmapMarkerSet,
|
type: Object as () => LiveAtlasMarkerSet,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
layerGroup: {
|
layerGroup: {
|
||||||
@ -49,9 +48,9 @@ export default defineComponent({
|
|||||||
layers = Object.freeze(new Map()) as Map<string, Marker>,
|
layers = Object.freeze(new Map()) as Map<string, Marker>,
|
||||||
|
|
||||||
createMarkers = () => {
|
createMarkers = () => {
|
||||||
const converter = getPointConverter();
|
const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
|
||||||
|
|
||||||
props.set.markers.forEach((marker: DynmapMarker, id: string) => {
|
props.set.markers.forEach((marker: LiveAtlasMarker, id: string) => {
|
||||||
const layer = createMarker(marker, converter);
|
const layer = createMarker(marker, converter);
|
||||||
|
|
||||||
layers.set(id, layer);
|
layers.set(id, layer);
|
||||||
@ -74,15 +73,14 @@ export default defineComponent({
|
|||||||
const updates = await useStore().dispatch(ActionTypes.POP_MARKER_UPDATES, {
|
const updates = await useStore().dispatch(ActionTypes.POP_MARKER_UPDATES, {
|
||||||
markerSet: props.set.id,
|
markerSet: props.set.id,
|
||||||
amount: 10,
|
amount: 10,
|
||||||
});
|
}),
|
||||||
|
converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
|
||||||
const converter = getPointConverter();
|
|
||||||
|
|
||||||
for(const update of updates) {
|
for(const update of updates) {
|
||||||
if(update.removed) {
|
if(update.removed) {
|
||||||
deleteMarker(update.id);
|
deleteMarker(update.id);
|
||||||
} else {
|
} else {
|
||||||
const layer = updateMarker(layers.get(update.id), update.payload as DynmapMarker, converter);
|
const layer = updateMarker(layers.get(update.id), update.payload as LiveAtlasMarker, converter);
|
||||||
|
|
||||||
if(!layers.has(update.id)) {
|
if(!layers.has(update.id)) {
|
||||||
props.layerGroup.addLayer(layer);
|
props.layerGroup.addLayer(layer);
|
||||||
@ -100,11 +98,12 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//FIXME: Prevent unnecessary repositioning when changing worlds
|
watch(currentMap, (newValue, oldValue) => {
|
||||||
watch(currentMap, (newValue) => {
|
if(newValue && (!oldValue || oldValue.world === newValue.world)) {
|
||||||
if(newValue) {
|
const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap);
|
||||||
|
|
||||||
for (const [id, marker] of props.set.markers) {
|
for (const [id, marker] of props.set.markers) {
|
||||||
updateMarker(layers.get(id), marker, getPointConverter());
|
updateMarker(layers.get(id), marker, converter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
<!--
|
||||||
|
- Copyright 2021 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.
|
||||||
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section :class="{'sidebar__section': true, 'section--collapsible': true, 'section--collapsed': collapsed}">
|
<section :class="{'sidebar__section': true, 'section--collapsible': true, 'section--collapsed': collapsed}">
|
||||||
<h2 class="section__heading">
|
<h2 class="section__heading">
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -21,7 +21,7 @@
|
|||||||
<div :class="{'following__target': true, 'following__target--hidden': target.hidden}">
|
<div :class="{'following__target': true, 'following__target--hidden': target.hidden}">
|
||||||
<img width="32" height="32" class="target__icon" :src="image" alt="" />
|
<img width="32" height="32" class="target__icon" :src="image" alt="" />
|
||||||
<span class="target__info">
|
<span class="target__info">
|
||||||
<span class="target__name" v-html="target.name"></span>
|
<span class="target__name" v-html="target.displayName"></span>
|
||||||
<span class="target__status" v-show="target.hidden">{{ messageHidden }}</span>
|
<span class="target__status" v-show="target.hidden">{{ messageHidden }}</span>
|
||||||
</span>
|
</span>
|
||||||
<button class="target__unfollow" type="button" :title="messageUnfollowTitle"
|
<button class="target__unfollow" type="button" :title="messageUnfollowTitle"
|
||||||
@ -31,25 +31,25 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {DynmapPlayer} from "@/dynmap";
|
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import {MutationTypes} from "@/store/mutation-types";
|
import {MutationTypes} from "@/store/mutation-types";
|
||||||
import {computed, defineComponent, onMounted, ref, watch} from "@vue/runtime-core";
|
import {computed, defineComponent, onMounted, ref, watch} from "@vue/runtime-core";
|
||||||
import {getMinecraftHead} from '@/util';
|
import {getMinecraftHead} from '@/util';
|
||||||
import defaultImage from '@/assets/images/player_face.png';
|
import defaultImage from '@/assets/images/player_face.png';
|
||||||
|
import {LiveAtlasPlayer} from "@/index";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'FollowTarget',
|
name: 'FollowTarget',
|
||||||
props: {
|
props: {
|
||||||
target: {
|
target: {
|
||||||
type: Object as () => DynmapPlayer,
|
type: Object as () => LiveAtlasPlayer,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const store = useStore(),
|
const store = useStore(),
|
||||||
image = ref(defaultImage),
|
image = ref(defaultImage),
|
||||||
account = ref(props.target.account),
|
account = ref(props.target.name),
|
||||||
|
|
||||||
heading = computed(() => store.state.messages.followingHeading),
|
heading = computed(() => store.state.messages.followingHeading),
|
||||||
messageUnfollow = computed(() => store.state.messages.followingUnfollow),
|
messageUnfollow = computed(() => store.state.messages.followingUnfollow),
|
||||||
@ -124,4 +124,4 @@ export default defineComponent({
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<CollapsibleSection name="players" class="players">
|
<CollapsibleSection name="players" class="players">
|
||||||
<template v-slot:heading>{{ messageHeading }} [{{ players.length }}/{{ maxPlayers }}]</template>
|
<template v-slot:heading>{{ messageHeading }}</template>
|
||||||
<template v-slot:default>
|
<template v-slot:default>
|
||||||
<div class="section__content">
|
<div class="section__content">
|
||||||
<input v-if="players && searchEnabled" id="players__search" type="text" name="search"
|
<input v-if="players && searchEnabled" id="players__search" type="text" name="search"
|
||||||
v-model="searchQuery" :placeholder="messagePlayersSearchPlaceholder" @keydown="onKeydown">
|
v-model="searchQuery" :placeholder="messagePlayersSearchPlaceholder" @keydown="onKeydown">
|
||||||
<RadioList v-if="filteredPlayers.length" aria-labelledby="players-heading">
|
<RadioList v-if="filteredPlayers.length" aria-labelledby="players-heading">
|
||||||
<PlayerListItem v-for="player in filteredPlayers" :key="player.account"
|
<PlayerListItem v-for="player in filteredPlayers" :key="player.name"
|
||||||
:player="player"></PlayerListItem>
|
:player="player"></PlayerListItem>
|
||||||
</RadioList>
|
</RadioList>
|
||||||
<div v-else-if="searchQuery" class="section__skeleton">{{ messageSkeletonPlayersSearch }}</div>
|
<div v-else-if="searchQuery" class="section__skeleton">{{ messageSkeletonPlayersSearch }}</div>
|
||||||
@ -49,7 +49,11 @@ export default defineComponent({
|
|||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const store = useStore(),
|
const store = useStore(),
|
||||||
messageHeading = computed(() => store.state.messages.playersHeading),
|
messageHeading = computed(() => {
|
||||||
|
return store.state.messages.playersHeading
|
||||||
|
.replace('{cur}', players.value.length.toString())
|
||||||
|
.replace('{max}', maxPlayers.value.toString());
|
||||||
|
}),
|
||||||
messageSkeletonPlayers = computed(() => store.state.messages.playersSkeleton),
|
messageSkeletonPlayers = computed(() => store.state.messages.playersSkeleton),
|
||||||
messageSkeletonPlayersSearch = computed(() => store.state.messages.playersSearchSkeleton),
|
messageSkeletonPlayersSearch = computed(() => store.state.messages.playersSearchSkeleton),
|
||||||
messagePlayersSearchPlaceholder = computed(() => store.state.messages.playersSearchPlaceholder),
|
messagePlayersSearchPlaceholder = computed(() => store.state.messages.playersSearchPlaceholder),
|
||||||
@ -62,10 +66,10 @@ export default defineComponent({
|
|||||||
const query = searchQuery.value.toLowerCase();
|
const query = searchQuery.value.toLowerCase();
|
||||||
|
|
||||||
return query ? store.state.sortedPlayers.filter(p => {
|
return query ? store.state.sortedPlayers.filter(p => {
|
||||||
return p.account.toLowerCase().indexOf(query) > -1;
|
return p.name.toLowerCase().indexOf(query) > -1;
|
||||||
}) : store.state.sortedPlayers;
|
}) : store.state.sortedPlayers;
|
||||||
}),
|
}),
|
||||||
maxPlayers = computed(() => store.state.configuration.maxPlayers),
|
maxPlayers = computed(() => store.state.maxPlayers || 0),
|
||||||
|
|
||||||
onKeydown = (e: KeyboardEvent) => {
|
onKeydown = (e: KeyboardEvent) => {
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
|
@ -1,50 +1,50 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<input :id="`player-${player.account}`" type="radio" name="player" v-bind:value="player.account" v-model="followTarget"
|
<input :id="`player-${player.name}`" type="radio" name="player" v-bind:value="player.name" v-model="followTarget"
|
||||||
@click.prevent="onInputClick" />
|
@click.prevent="onInputClick" />
|
||||||
<label :for="`player-${player.account}`"
|
<label :for="`player-${player.name}`"
|
||||||
:class="{'player': true, 'player--hidden' : !!player.hidden, 'player--other-world': otherWorld}" :title="title"
|
:class="{'player': true, 'player--hidden' : !!player.hidden, 'player--other-world': otherWorld}" :title="title"
|
||||||
@click.prevent="onLabelClick">
|
@click.prevent="onLabelClick">
|
||||||
<img width="16" height="16" class="player__icon" :src="image" alt="" aria-hidden="true" />
|
<img width="16" height="16" class="player__icon" :src="image" alt="" aria-hidden="true" />
|
||||||
<span class="player__name" v-html="player.name"></span>
|
<span class="player__name" v-html="player.displayName"></span>
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, computed, ref, onMounted} from 'vue';
|
import {defineComponent, computed, ref, onMounted} from 'vue';
|
||||||
import {DynmapPlayer} from "@/dynmap";
|
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import {MutationTypes} from "@/store/mutation-types";
|
import {MutationTypes} from "@/store/mutation-types";
|
||||||
import {getMinecraftHead} from '@/util';
|
import {getMinecraftHead} from '@/util';
|
||||||
import defaultImage from '@/assets/images/player_face.png';
|
import defaultImage from '@/assets/images/player_face.png';
|
||||||
|
import {LiveAtlasPlayer} from "@/index";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'PlayerListItem',
|
name: 'PlayerListItem',
|
||||||
props: {
|
props: {
|
||||||
player: {
|
player: {
|
||||||
type: Object as () => DynmapPlayer,
|
type: Object as () => LiveAtlasPlayer,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const store = useStore(),
|
const store = useStore(),
|
||||||
otherWorld = computed(() => {
|
otherWorld = computed(() => {
|
||||||
return store.state.configuration.grayHiddenPlayers
|
return store.state.components.playerMarkers?.grayHiddenPlayers
|
||||||
&& !props.player.hidden
|
&& !props.player.hidden
|
||||||
&& (!store.state.currentWorld || store.state.currentWorld.name !== props.player.location.world);
|
&& (!store.state.currentWorld || store.state.currentWorld.name !== props.player.location.world);
|
||||||
}),
|
}),
|
||||||
@ -59,7 +59,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
followTarget = computed(() => store.state.followTarget ? store.state.followTarget.account : undefined),
|
followTarget = computed(() => store.state.followTarget ? store.state.followTarget.name : undefined),
|
||||||
|
|
||||||
pan = () => {
|
pan = () => {
|
||||||
if(!props.player.hidden) {
|
if(!props.player.hidden) {
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="world">
|
<div class="world">
|
||||||
<span class="world__name" aria-hidden="true">{{ world.title }}</span>
|
<span class="world__name" aria-hidden="true">{{ world.displayName }}</span>
|
||||||
<div class="world__maps menu">
|
<div class="world__maps menu">
|
||||||
<template v-for="[key, map] in world.maps" :key="`${world.name}_${key}`">
|
<template v-for="[key, map] in world.maps" :key="`${world.name}_${key}`">
|
||||||
<input :id="`${name}-${world.name}-${key}`" type="radio" :name="name"
|
<input :id="`${name}-${world.name}-${key}`" type="radio" :name="name"
|
||||||
v-bind:value="[world.name,map.name]" v-model="currentMap"
|
v-bind:value="[world.name,map.name]" v-model="currentMap"
|
||||||
:aria-labelledby="`${name}-${world.name}-${key}-label`">
|
:aria-labelledby="`${name}-${world.name}-${key}-label`">
|
||||||
<label :id="`${name}-${world.name}-${key}-label`" class="map" :for="`${name}-${world.name}-${key}`" :title="`${world.title} - ${map.title}`">
|
<label :id="`${name}-${world.name}-${key}-label`" class="map" :for="`${name}-${world.name}-${key}`" :title="`${world.displayName} - ${map.displayName}`">
|
||||||
<SvgIcon :name="map.getIcon()"></SvgIcon>
|
<SvgIcon :name="map.getIcon()"></SvgIcon>
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<!--
|
<!--
|
||||||
- Copyright 2020 James Lyne
|
- Copyright 2021 James Lyne
|
||||||
-
|
-
|
||||||
- Licensed under the Apache License, Version 2.0 (the "License");
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
- you may not use this file except in compliance with the License.
|
- you may not use this file except in compliance with the License.
|
||||||
- You may obtain a copy of the License at
|
- You may obtain a copy of the License at
|
||||||
-
|
-
|
||||||
- http://www.apache.org/licenses/LICENSE-2.0
|
- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
-
|
-
|
||||||
- Unless required by applicable law or agreed to in writing, software
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
- See the License for the specific language governing permissions and
|
- See the License for the specific language governing permissions and
|
||||||
- limitations under the License.
|
- limitations under the License.
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<fieldset class="menu" role="radiogroup" @keydown="onKeydown">
|
<fieldset class="menu" role="radiogroup" @keydown="onKeydown">
|
||||||
|
199
src/dynmap.d.ts
vendored
199
src/dynmap.d.ts
vendored
@ -1,30 +1,20 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {PathOptions, PointTuple, PolylineOptions} from "leaflet";
|
import {LiveAtlasArea, LiveAtlasCircle, LiveAtlasLine, LiveAtlasMarker} from "@/index";
|
||||||
import {CoordinatesControlOptions} from "@/leaflet/control/CoordinatesControl";
|
|
||||||
import {LogoControlOptions} from "@/leaflet/control/LogoControl";
|
|
||||||
import {ClockControlOptions} from "@/leaflet/control/ClockControl";
|
|
||||||
import {
|
|
||||||
Coordinate,
|
|
||||||
LiveAtlasLocation,
|
|
||||||
LiveAtlasServerMessageConfig,
|
|
||||||
LiveAtlasWorldDefinition,
|
|
||||||
LiveAtlasWorldState
|
|
||||||
} from "@/index";
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// noinspection JSUnusedGlobalSymbols
|
// noinspection JSUnusedGlobalSymbols
|
||||||
@ -43,155 +33,6 @@ type DynmapUrlConfig = {
|
|||||||
markers: string;
|
markers: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DynmapServerConfig {
|
|
||||||
version: string;
|
|
||||||
defaultMap?: string;
|
|
||||||
defaultWorld?: string;
|
|
||||||
defaultZoom: number;
|
|
||||||
followMap?: string;
|
|
||||||
followZoom: number;
|
|
||||||
updateInterval: number;
|
|
||||||
showLayerControl: boolean;
|
|
||||||
title: string;
|
|
||||||
loginEnabled: boolean;
|
|
||||||
maxPlayers: number;
|
|
||||||
grayHiddenPlayers: boolean;
|
|
||||||
expandUI: boolean;
|
|
||||||
hash: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynmapComponentConfig {
|
|
||||||
markers: DynmapMarkersConfig;
|
|
||||||
playerMarkers?: DynmapPlayerMarkersConfig;
|
|
||||||
coordinatesControl?: CoordinatesControlOptions;
|
|
||||||
clockControl ?: ClockControlOptions;
|
|
||||||
linkControl: boolean;
|
|
||||||
logoControls: Array<LogoControlOptions>;
|
|
||||||
chatBox?: DynmapChatBoxConfig;
|
|
||||||
chatSending?: DynmapChatSendingConfig;
|
|
||||||
chatBalloons: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynmapMarkersConfig {
|
|
||||||
showLabels: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynmapPlayerMarkersConfig {
|
|
||||||
hideByDefault: boolean;
|
|
||||||
layerName: string;
|
|
||||||
layerPriority: number;
|
|
||||||
showBodies: boolean;
|
|
||||||
showSkinFaces: boolean;
|
|
||||||
showHealth: boolean;
|
|
||||||
smallFaces: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynmapChatBoxConfig {
|
|
||||||
allowUrlName: boolean;
|
|
||||||
showPlayerFaces: boolean;
|
|
||||||
messageLifetime: number;
|
|
||||||
messageHistory: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynmapChatSendingConfig {
|
|
||||||
loginRequired: boolean;
|
|
||||||
maxLength: number;
|
|
||||||
cooldown: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynmapConfigurationResponse {
|
|
||||||
config: DynmapServerConfig,
|
|
||||||
messages: LiveAtlasServerMessageConfig,
|
|
||||||
worlds: Array<LiveAtlasWorldDefinition>,
|
|
||||||
components: DynmapComponentConfig,
|
|
||||||
loggedIn: boolean,
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynmapUpdateResponse {
|
|
||||||
worldState: LiveAtlasWorldState;
|
|
||||||
configHash: number;
|
|
||||||
playerCount: number;
|
|
||||||
players: Set<DynmapPlayer>;
|
|
||||||
updates: DynmapUpdates;
|
|
||||||
timestamp: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynmapPlayer {
|
|
||||||
account: string;
|
|
||||||
armor: number;
|
|
||||||
health: number;
|
|
||||||
name: string;
|
|
||||||
sort: number;
|
|
||||||
hidden: boolean;
|
|
||||||
location: LiveAtlasLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynmapMarkerSet {
|
|
||||||
id: string,
|
|
||||||
label: string;
|
|
||||||
hidden: boolean;
|
|
||||||
priority: number;
|
|
||||||
minZoom?: number;
|
|
||||||
maxZoom?: number;
|
|
||||||
showLabels?: boolean;
|
|
||||||
markers: Map<string, DynmapMarker>;
|
|
||||||
areas: Map<string, DynmapArea>;
|
|
||||||
lines: Map<string, DynmapLine>;
|
|
||||||
circles: Map<string, DynmapCircle>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynmapMarker {
|
|
||||||
dimensions: PointTuple;
|
|
||||||
icon: string;
|
|
||||||
label: string;
|
|
||||||
isHTML: boolean;
|
|
||||||
location: Coordinate;
|
|
||||||
minZoom?: number;
|
|
||||||
maxZoom?: number;
|
|
||||||
popupContent?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynmapArea {
|
|
||||||
style: PolylineOptions;
|
|
||||||
label: string;
|
|
||||||
isHTML: boolean;
|
|
||||||
x: Array<number>;
|
|
||||||
y: PointTuple;
|
|
||||||
z: Array<number>;
|
|
||||||
minZoom?: number;
|
|
||||||
maxZoom?: number;
|
|
||||||
popupContent?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynmapLine {
|
|
||||||
x: Array<number>;
|
|
||||||
y: Array<number>;
|
|
||||||
z: Array<number>;
|
|
||||||
style: PolylineOptions;
|
|
||||||
label: string;
|
|
||||||
isHTML: boolean;
|
|
||||||
minZoom?: number;
|
|
||||||
maxZoom?: number;
|
|
||||||
popupContent?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynmapCircle {
|
|
||||||
location: Coordinate;
|
|
||||||
radius: PointTuple;
|
|
||||||
style: PathOptions;
|
|
||||||
label: string;
|
|
||||||
isHTML: boolean;
|
|
||||||
minZoom?: number;
|
|
||||||
maxZoom?: number;
|
|
||||||
popupContent?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynmapUpdates {
|
|
||||||
markerSets: Map<string, DynmapMarkerSetUpdates>,
|
|
||||||
tiles: Array<DynmapTileUpdate>,
|
|
||||||
chat: Array<any> //TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynmapMarkerSetUpdates {
|
interface DynmapMarkerSetUpdates {
|
||||||
markerUpdates: Array<DynmapMarkerUpdate>
|
markerUpdates: Array<DynmapMarkerUpdate>
|
||||||
areaUpdates: Array<DynmapAreaUpdate>
|
areaUpdates: Array<DynmapAreaUpdate>
|
||||||
@ -215,32 +56,22 @@ interface DynmapUpdate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface DynmapMarkerUpdate extends DynmapUpdate {
|
interface DynmapMarkerUpdate extends DynmapUpdate {
|
||||||
payload?: DynmapMarker
|
payload?: LiveAtlasMarker
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DynmapAreaUpdate extends DynmapUpdate {
|
interface DynmapAreaUpdate extends DynmapUpdate {
|
||||||
payload?: DynmapArea
|
payload?: LiveAtlasArea
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DynmapCircleUpdate extends DynmapUpdate {
|
interface DynmapCircleUpdate extends DynmapUpdate {
|
||||||
payload?: DynmapCircle
|
payload?: LiveAtlasCircle
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DynmapLineUpdate extends DynmapUpdate {
|
interface DynmapLineUpdate extends DynmapUpdate {
|
||||||
payload?: DynmapLine
|
payload?: LiveAtlasLine
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DynmapTileUpdate {
|
interface DynmapTileUpdate {
|
||||||
name: string
|
name: string
|
||||||
timestamp: number
|
timestamp: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DynmapChat {
|
|
||||||
type: 'chat' | 'playerjoin' | 'playerleave';
|
|
||||||
playerAccount?: string;
|
|
||||||
playerName?: string;
|
|
||||||
channel?: string;
|
|
||||||
message?: string;
|
|
||||||
source?: string;
|
|
||||||
timestamp: number;
|
|
||||||
}
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2021 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default class ChatError extends Error {
|
export default class ChatError extends Error {
|
||||||
@ -19,4 +19,4 @@ export default class ChatError extends Error {
|
|||||||
super(message);
|
super(message);
|
||||||
this.name = "ChatError";
|
this.name = "ChatError";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2021 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default class ConfigurationError extends Error {
|
export default class ConfigurationError extends Error {
|
||||||
@ -19,4 +19,4 @@ export default class ConfigurationError extends Error {
|
|||||||
super(message);
|
super(message);
|
||||||
this.name = "";
|
this.name = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
197
src/index.d.ts
vendored
197
src/index.d.ts
vendored
@ -1,6 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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 {State} from "@/store";
|
import {State} from "@/store";
|
||||||
import {DynmapPlayer, DynmapUrlConfig} from "@/dynmap";
|
import {DynmapUrlConfig} from "@/dynmap";
|
||||||
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
||||||
|
import {PathOptions, PointTuple, PolylineOptions} from "leaflet";
|
||||||
|
import {CoordinatesControlOptions} from "@/leaflet/control/CoordinatesControl";
|
||||||
|
import {ClockControlOptions} from "@/leaflet/control/ClockControl";
|
||||||
|
import {LogoControlOptions} from "@/leaflet/control/LogoControl";
|
||||||
|
|
||||||
declare module "*.png" {
|
declare module "*.png" {
|
||||||
const value: any;
|
const value: any;
|
||||||
@ -40,13 +60,11 @@ interface LiveAtlasGlobalConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface LiveAtlasServerDefinition {
|
interface LiveAtlasServerDefinition {
|
||||||
id: string
|
id: string;
|
||||||
label?: string
|
label?: string;
|
||||||
}
|
type: 'dynmap' | 'pl3xmap';
|
||||||
|
dynmap?: DynmapUrlConfig;
|
||||||
interface LiveAtlasDynmapServerDefinition extends LiveAtlasServerDefinition {
|
pl3xmap?: string;
|
||||||
type: 'dynmap',
|
|
||||||
dynmap: DynmapUrlConfig,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Messages defined directly in LiveAtlas and used for all servers
|
// Messages defined directly in LiveAtlas and used for all servers
|
||||||
@ -108,16 +126,27 @@ export type LiveAtlasUIElement = 'layers' | 'chat' | 'players' | 'maps' | 'setti
|
|||||||
export type LiveAtlasSidebarSection = 'servers' | 'players' | 'maps';
|
export type LiveAtlasSidebarSection = 'servers' | 'players' | 'maps';
|
||||||
export type LiveAtlasDimension = 'overworld' | 'nether' | 'end';
|
export type LiveAtlasDimension = 'overworld' | 'nether' | 'end';
|
||||||
|
|
||||||
interface LiveAtlasSortedPlayers extends Array<DynmapPlayer> {
|
interface LiveAtlasPlayer {
|
||||||
|
name: string;
|
||||||
|
displayName: string;
|
||||||
|
uuid?: string;
|
||||||
|
armor: number;
|
||||||
|
health: number;
|
||||||
|
sort: number;
|
||||||
|
hidden: boolean;
|
||||||
|
location: LiveAtlasLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiveAtlasSortedPlayers extends Array<LiveAtlasPlayer> {
|
||||||
dirty?: boolean;
|
dirty?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LiveAtlasWorldDefinition {
|
interface LiveAtlasWorldDefinition {
|
||||||
seaLevel: number;
|
seaLevel: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
displayName: string;
|
||||||
dimension: LiveAtlasDimension;
|
dimension: LiveAtlasDimension;
|
||||||
protected: boolean;
|
protected: boolean;
|
||||||
title: string;
|
|
||||||
height: number;
|
height: number;
|
||||||
center: Coordinate;
|
center: Coordinate;
|
||||||
maps: Map<string, LiveAtlasMapDefinition>;
|
maps: Map<string, LiveAtlasMapDefinition>;
|
||||||
@ -136,3 +165,151 @@ interface LiveAtlasParsedUrl {
|
|||||||
zoom?: number;
|
zoom?: number;
|
||||||
legacy: boolean;
|
legacy: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface LiveAtlasMapProvider {
|
||||||
|
loadServerConfiguration(): Promise<void>;
|
||||||
|
populateWorld(world: LiveAtlasWorldDefinition): Promise<void>;
|
||||||
|
startUpdates(): void;
|
||||||
|
stopUpdates(): void;
|
||||||
|
sendChatMessage(message: string): void;
|
||||||
|
destroy(): void;
|
||||||
|
|
||||||
|
getPlayerHeadUrl(entry: HeadQueueEntry): string;
|
||||||
|
getTilesUrl(): string;
|
||||||
|
getMarkerIconUrl(icon: string): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiveAtlasMarkerSet {
|
||||||
|
id: string,
|
||||||
|
label: string;
|
||||||
|
hidden: boolean;
|
||||||
|
priority: number;
|
||||||
|
minZoom?: number;
|
||||||
|
maxZoom?: number;
|
||||||
|
showLabels?: boolean;
|
||||||
|
markers: Map<string, LiveAtlasMarker>;
|
||||||
|
areas: Map<string, LiveAtlasArea>;
|
||||||
|
lines: Map<string, LiveAtlasLine>;
|
||||||
|
circles: Map<string, LiveAtlasCircle>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiveAtlasMarker {
|
||||||
|
dimensions: PointTuple;
|
||||||
|
icon: string;
|
||||||
|
label: string;
|
||||||
|
isLabelHTML: boolean;
|
||||||
|
location: Coordinate;
|
||||||
|
minZoom?: number;
|
||||||
|
maxZoom?: number;
|
||||||
|
popupContent?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiveAtlasPath {
|
||||||
|
style: PathOptions;
|
||||||
|
minZoom?: number;
|
||||||
|
maxZoom?: number;
|
||||||
|
popupContent?: string;
|
||||||
|
tooltipContent?: string;
|
||||||
|
isPopupHTML: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiveAtlasArea extends LiveAtlasPath {
|
||||||
|
style: PolylineOptions;
|
||||||
|
outline: boolean;
|
||||||
|
points: Coordinate[] | Coordinate[][] | Coordinate[][][]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiveAtlasLine extends LiveAtlasPath {
|
||||||
|
points: Coordinate[];
|
||||||
|
style: PolylineOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiveAtlasCircle extends LiveAtlasPath {
|
||||||
|
location: Coordinate;
|
||||||
|
radius: PointTuple;
|
||||||
|
style: PathOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HeadQueueEntry {
|
||||||
|
cacheKey: string;
|
||||||
|
name: string;
|
||||||
|
uuid?: string;
|
||||||
|
size: string;
|
||||||
|
image: HTMLImageElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiveAtlasServerConfig {
|
||||||
|
defaultMap?: string;
|
||||||
|
defaultWorld?: string;
|
||||||
|
defaultZoom: number;
|
||||||
|
followMap?: string;
|
||||||
|
followZoom?: number;
|
||||||
|
title: string;
|
||||||
|
expandUI: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiveAtlasComponentConfig {
|
||||||
|
markers: {
|
||||||
|
showLabels: boolean;
|
||||||
|
};
|
||||||
|
playerMarkers?: LiveAtlasPlayerMarkerConfig;
|
||||||
|
coordinatesControl?: CoordinatesControlOptions;
|
||||||
|
clockControl?: ClockControlOptions;
|
||||||
|
linkControl: boolean;
|
||||||
|
layerControl: boolean;
|
||||||
|
logoControls: Array<LogoControlOptions>;
|
||||||
|
chatBox?: LiveAtlasChatBoxConfig;
|
||||||
|
chatSending?: LiveAtlasChatSendingConfig;
|
||||||
|
chatBalloons: boolean;
|
||||||
|
login: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiveAtlasPartialComponentConfig {
|
||||||
|
markers?: {
|
||||||
|
showLabels: boolean;
|
||||||
|
};
|
||||||
|
playerMarkers?: LiveAtlasPlayerMarkerConfig;
|
||||||
|
coordinatesControl?: CoordinatesControlOptions;
|
||||||
|
clockControl?: ClockControlOptions;
|
||||||
|
linkControl?: boolean;
|
||||||
|
layerControl?: boolean;
|
||||||
|
logoControls?: Array<LogoControlOptions>;
|
||||||
|
chatBox?: LiveAtlasChatBoxConfig;
|
||||||
|
chatSending?: LiveAtlasChatSendingConfig;
|
||||||
|
chatBalloons?: boolean;
|
||||||
|
login?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiveAtlasPlayerMarkerConfig {
|
||||||
|
grayHiddenPlayers: boolean;
|
||||||
|
hideByDefault: boolean;
|
||||||
|
layerName: string;
|
||||||
|
layerPriority: number;
|
||||||
|
showBodies: boolean;
|
||||||
|
showSkinFaces: boolean;
|
||||||
|
showHealth: boolean;
|
||||||
|
smallFaces: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiveAtlasChatBoxConfig {
|
||||||
|
allowUrlName: boolean;
|
||||||
|
showPlayerFaces: boolean;
|
||||||
|
messageLifetime: number;
|
||||||
|
messageHistory: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiveAtlasChatSendingConfig {
|
||||||
|
loginRequired: boolean;
|
||||||
|
maxLength: number;
|
||||||
|
cooldown: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiveAtlasChat {
|
||||||
|
type: 'chat' | 'playerjoin' | 'playerleave';
|
||||||
|
playerAccount?: string;
|
||||||
|
playerName?: string;
|
||||||
|
channel?: string;
|
||||||
|
message?: string;
|
||||||
|
source?: string;
|
||||||
|
timestamp: number;
|
||||||
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Map, DomUtil, MapOptions} from 'leaflet';
|
import {Map, DomUtil, MapOptions} from 'leaflet';
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Control, ControlOptions, DomEvent, DomUtil} from 'leaflet';
|
import {Control, ControlOptions, DomEvent, DomUtil} from 'leaflet';
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
||||||
* These portions are Copyright 2020 Dynmap Contributors.
|
* These portions are Copyright 2020 Dynmap Contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ControlOptions, DomUtil, Util, Control} from 'leaflet';
|
import {ControlOptions, DomUtil, Util, Control} from 'leaflet';
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
||||||
* These portions are Copyright 2020 Dynmap Contributors.
|
* These portions are Copyright 2020 Dynmap Contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ControlOptions, LeafletMouseEvent, Control, Map, DomUtil, Util} from 'leaflet';
|
import {ControlOptions, LeafletMouseEvent, Control, Map, DomUtil, Util} from 'leaflet';
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
||||||
* These portions are Copyright 2020 Dynmap Contributors.
|
* These portions are Copyright 2020 Dynmap Contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Control, ControlOptions, DomUtil} from 'leaflet';
|
import {Control, ControlOptions, DomUtil} from 'leaflet';
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
||||||
* These portions are Copyright 2020 Dynmap Contributors.
|
* These portions are Copyright 2020 Dynmap Contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Control, DomEvent, DomUtil, Layer, LeafletEvent, Map as LeafletMap, Util} from 'leaflet';
|
import {Control, DomEvent, DomUtil, Layer, LeafletEvent, Map as LeafletMap, Util} from 'leaflet';
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
||||||
* These portions are Copyright 2020 Dynmap Contributors.
|
* These portions are Copyright 2020 Dynmap Contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Control, ControlOptions, DomUtil} from 'leaflet';
|
import {Control, ControlOptions, DomUtil} from 'leaflet';
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
||||||
* These portions are Copyright 2020 Dynmap Contributors.
|
* These portions are Copyright 2020 Dynmap Contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {DivIconOptions, PointExpression, Icon, DivIcon, DomUtil, point} from 'leaflet';
|
import {DivIconOptions, PointExpression, Icon, DivIcon, DomUtil, point} from 'leaflet';
|
||||||
@ -47,8 +47,14 @@ export class GenericIcon extends DivIcon {
|
|||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
options: GenericIconOptions;
|
options: GenericIconOptions;
|
||||||
_image?: HTMLImageElement;
|
|
||||||
_label?: HTMLSpanElement;
|
private _image?: HTMLImageElement;
|
||||||
|
private _label?: HTMLSpanElement;
|
||||||
|
private _container?: HTMLDivElement;
|
||||||
|
private _labelCreated: boolean = false;
|
||||||
|
private _onHover: EventListener = () => {
|
||||||
|
this.createLabel();
|
||||||
|
};
|
||||||
|
|
||||||
constructor(options: GenericIconOptions) {
|
constructor(options: GenericIconOptions) {
|
||||||
super(Object.assign(GenericIcon.defaultOptions, options));
|
super(Object.assign(GenericIcon.defaultOptions, options));
|
||||||
@ -60,18 +66,45 @@ export class GenericIcon extends DivIcon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const div = markerContainer.cloneNode(false) as HTMLDivElement,
|
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);
|
size = point(this.options.iconSize as PointExpression);
|
||||||
|
|
||||||
this._image = markerIcon.cloneNode(false) as HTMLImageElement;
|
this._image = markerIcon.cloneNode(false) as HTMLImageElement;
|
||||||
this._label = markerLabel.cloneNode(false) as HTMLSpanElement;
|
|
||||||
|
|
||||||
const sizeClass = [size.x, size.y].join('x');
|
|
||||||
|
|
||||||
this._image.width = size.x;
|
this._image.width = size.x;
|
||||||
this._image.height = size.y;
|
this._image.height = size.y;
|
||||||
this._image.src = url;
|
this._image.src = url;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
Icon.prototype._setIconStyles.call(this, div, 'icon');
|
||||||
|
|
||||||
|
div.appendChild(this._image);
|
||||||
|
div.classList.add('marker');
|
||||||
|
|
||||||
|
if(this.options.className) {
|
||||||
|
div.classList.add(this.options.className);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create label lazily on hover
|
||||||
|
this._image.addEventListener('mouseover', this._onHover);
|
||||||
|
|
||||||
|
this._container = div;
|
||||||
|
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
|
||||||
|
createLabel() {
|
||||||
|
if(!this._container || this._labelCreated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._image?.removeEventListener('mouseover', this._onHover);
|
||||||
|
|
||||||
|
const size = point(this.options.iconSize as PointExpression),
|
||||||
|
sizeClass = [size.x, size.y].join('x');
|
||||||
|
|
||||||
|
this._label = markerLabel.cloneNode(false) as HTMLSpanElement;
|
||||||
|
|
||||||
this._label.classList.add(/*'markerName_' + set.id,*/ `marker__label--${sizeClass}`);
|
this._label.classList.add(/*'markerName_' + set.id,*/ `marker__label--${sizeClass}`);
|
||||||
|
|
||||||
if (this.options.isHtml) {
|
if (this.options.isHtml) {
|
||||||
@ -80,23 +113,13 @@ export class GenericIcon extends DivIcon {
|
|||||||
this._label.textContent = this.options.label;
|
this._label.textContent = this.options.label;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
this._container!.appendChild(this._label);
|
||||||
Icon.prototype._setIconStyles.call(this, div, 'icon');
|
this._labelCreated = true;
|
||||||
|
|
||||||
div.appendChild(this._image);
|
|
||||||
div.appendChild(this._label);
|
|
||||||
div.classList.add('marker');
|
|
||||||
|
|
||||||
if(this.options.className) {
|
|
||||||
div.classList.add(this.options.className);
|
|
||||||
}
|
|
||||||
|
|
||||||
return div;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update(options: GenericIconOptions) {
|
update(options: GenericIconOptions) {
|
||||||
if(this._image && options.icon !== this.options.icon) {
|
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;
|
this.options.icon = options.icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
||||||
* These portions are Copyright 2020 Dynmap Contributors.
|
* These portions are Copyright 2020 Dynmap Contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {MarkerOptions, DivIcon, DomUtil} from 'leaflet';
|
import {MarkerOptions, DivIcon, DomUtil} from 'leaflet';
|
||||||
import {DynmapPlayer} from "@/dynmap";
|
|
||||||
import {getMinecraftHead} from '@/util';
|
import {getMinecraftHead} from '@/util';
|
||||||
import playerImage from '@/assets/images/player_face.png';
|
import playerImage from '@/assets/images/player_face.png';
|
||||||
|
import {LiveAtlasPlayer} from "@/index";
|
||||||
|
|
||||||
const noSkinImage: HTMLImageElement = document.createElement('img');
|
const noSkinImage: HTMLImageElement = document.createElement('img');
|
||||||
noSkinImage.height = 16;
|
noSkinImage.height = 16;
|
||||||
@ -49,7 +49,7 @@ export interface PlayerIconOptions extends MarkerOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PlayerIcon extends DivIcon {
|
export class PlayerIcon extends DivIcon {
|
||||||
private readonly _player: DynmapPlayer;
|
private readonly _player: LiveAtlasPlayer;
|
||||||
private _container?: HTMLDivElement;
|
private _container?: HTMLDivElement;
|
||||||
private _playerImage?: HTMLImageElement;
|
private _playerImage?: HTMLImageElement;
|
||||||
private _playerInfo?: HTMLSpanElement;
|
private _playerInfo?: HTMLSpanElement;
|
||||||
@ -65,7 +65,7 @@ export class PlayerIcon extends DivIcon {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
options: PlayerIconOptions;
|
options: PlayerIconOptions;
|
||||||
|
|
||||||
constructor(player: DynmapPlayer, options: PlayerIconOptions) {
|
constructor(player: LiveAtlasPlayer, options: PlayerIconOptions) {
|
||||||
super(options);
|
super(options);
|
||||||
this._player = player;
|
this._player = player;
|
||||||
}
|
}
|
||||||
@ -87,7 +87,7 @@ export class PlayerIcon extends DivIcon {
|
|||||||
|
|
||||||
this._playerName = document.createElement('span');
|
this._playerName = document.createElement('span');
|
||||||
this._playerName.className = 'player__name';
|
this._playerName.className = 'player__name';
|
||||||
this._playerName.innerHTML = this._currentName = player.name;
|
this._playerName.innerHTML = this._currentName = player.displayName;
|
||||||
|
|
||||||
if (this.options.showSkinFace) {
|
if (this.options.showSkinFace) {
|
||||||
let size;
|
let size;
|
||||||
@ -151,8 +151,8 @@ export class PlayerIcon extends DivIcon {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this._player!.name !== this._currentName) {
|
if(this._player!.displayName !== this._currentName) {
|
||||||
this._playerName!.innerHTML = this._currentName = this._player!.name;
|
this._playerName!.innerHTML = this._currentName = this._player!.displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.options.showHealth) {
|
if(this.options.showHealth) {
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Map, Layer} from 'leaflet';
|
import {Map, Layer} from 'leaflet';
|
||||||
@ -25,7 +25,7 @@ export default class LayerManager {
|
|||||||
private readonly map: Map;
|
private readonly map: Map;
|
||||||
|
|
||||||
constructor(map: Map) {
|
constructor(map: Map) {
|
||||||
const showControl = computed(() => useStore().state.configuration.showLayerControl);
|
const showControl = computed(() => useStore().state.components.layerControl);
|
||||||
this.map = map;
|
this.map = map;
|
||||||
this.layerControl = new LiveAtlasLayerControl({}, {},{
|
this.layerControl = new LiveAtlasLayerControl({}, {},{
|
||||||
position: 'topleft',
|
position: 'topleft',
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Layer, Map as LeafletMap, LayerGroup, LayerOptions, Util, Marker, Path} from "leaflet";
|
import {Layer, Map as LeafletMap, LayerGroup, LayerOptions, Util, Marker, Path} from "leaflet";
|
||||||
|
import {GenericMarker} from "@/leaflet/marker/GenericMarker";
|
||||||
|
|
||||||
export interface LiveAtlasLayerGroupOptions extends LayerOptions {
|
export interface LiveAtlasLayerGroupOptions extends LayerOptions {
|
||||||
id: string; //Added to the name of layer group panes
|
id: string; //Added to the name of layer group panes
|
||||||
@ -29,11 +30,11 @@ export interface LiveAtlasLayerGroupOptions extends LayerOptions {
|
|||||||
export default class LiveAtlasLayerGroup extends LayerGroup {
|
export default class LiveAtlasLayerGroup extends LayerGroup {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
options: LiveAtlasLayerGroupOptions;
|
options: LiveAtlasLayerGroupOptions;
|
||||||
_zoomLimitedLayers: Set<Layer>; //Layers which are zoom limited and should be checked on zoom
|
private _zoomLimitedLayers: Set<Layer>; //Layers which are zoom limited and should be checked on zoom
|
||||||
_layers: any;
|
_layers: any;
|
||||||
_markerPane?: HTMLElement;
|
_markerPane?: HTMLElement;
|
||||||
|
|
||||||
_zoomEndCallback = () => this._updateLayerVisibility();
|
private _zoomEndCallback = () => this._updateLayerVisibility();
|
||||||
|
|
||||||
constructor(options: LiveAtlasLayerGroupOptions) {
|
constructor(options: LiveAtlasLayerGroupOptions) {
|
||||||
super([], options);
|
super([], options);
|
||||||
@ -80,7 +81,7 @@ export default class LiveAtlasLayerGroup extends LayerGroup {
|
|||||||
layer.options.pane = `vectors`;
|
layer.options.pane = `vectors`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const zoomLimited = this._isLayerZoomLimited(layer);
|
const zoomLimited = LiveAtlasLayerGroup._isLayerZoomLimited(layer);
|
||||||
|
|
||||||
if (zoomLimited) {
|
if (zoomLimited) {
|
||||||
this._zoomLimitedLayers.add(layer);
|
this._zoomLimitedLayers.add(layer);
|
||||||
@ -89,11 +90,11 @@ export default class LiveAtlasLayerGroup extends LayerGroup {
|
|||||||
if (this._map) {
|
if (this._map) {
|
||||||
//If layer is zoom limited, only add to map if it should be visible
|
//If layer is zoom limited, only add to map if it should be visible
|
||||||
if (zoomLimited) {
|
if (zoomLimited) {
|
||||||
if (this._isLayerVisible(layer, this._map.getZoom())) {
|
if (LiveAtlasLayerGroup._isLayerVisible(layer, this._map.getZoom())) {
|
||||||
this._map.addLayer(layer);
|
this._addToMap(layer);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this._map.addLayer(layer);
|
this._addToMap(layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +107,19 @@ export default class LiveAtlasLayerGroup extends LayerGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update(options: LiveAtlasLayerGroupOptions) {
|
update(options: LiveAtlasLayerGroupOptions) {
|
||||||
this.options.showLabels = options.showLabels;
|
if(this.options.showLabels !== options.showLabels) {
|
||||||
|
//Create labels if they are now always visible
|
||||||
|
//TODO: This will be slow when many markers exist. Is it worth doing?
|
||||||
|
if(options.showLabels) {
|
||||||
|
this.eachLayer((layer) => {
|
||||||
|
if(layer instanceof GenericMarker) {
|
||||||
|
(layer as GenericMarker).createLabel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.options.showLabels = options.showLabels;
|
||||||
|
}
|
||||||
|
|
||||||
if(this._markerPane) {
|
if(this._markerPane) {
|
||||||
this._markerPane.classList.toggle('leaflet-pane--show-labels', options.showLabels);
|
this._markerPane.classList.toggle('leaflet-pane--show-labels', options.showLabels);
|
||||||
@ -128,7 +141,7 @@ export default class LiveAtlasLayerGroup extends LayerGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateLayerVisibility(onAdd?: boolean) {
|
private _updateLayerVisibility(onAdd?: boolean) {
|
||||||
if(!this._map) {
|
if(!this._map) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -142,34 +155,34 @@ export default class LiveAtlasLayerGroup extends LayerGroup {
|
|||||||
this.eachLayer((layer) => {
|
this.eachLayer((layer) => {
|
||||||
//Per marker zoom limits take precedence, if present
|
//Per marker zoom limits take precedence, if present
|
||||||
if(this._zoomLimitedLayers.has(layer)) {
|
if(this._zoomLimitedLayers.has(layer)) {
|
||||||
this._isLayerVisible(layer, zoom) ? this._map.addLayer(layer) : this._map.removeLayer(layer);
|
LiveAtlasLayerGroup._isLayerVisible(layer, zoom) ? this._addToMap(layer) : this._removeFromMap(layer);
|
||||||
} else { //Otherwise apply group zoom limit
|
} else { //Otherwise apply group zoom limit
|
||||||
visible ? this._map.addLayer(layer) : this._map.removeLayer(layer);
|
visible ? this._addToMap(layer) : this._removeFromMap(layer);
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
//Group isn't zoom limited, but some individual markers are
|
//Group isn't zoom limited, but some individual markers are
|
||||||
} else if(this._zoomLimitedLayers.size) {
|
} else if(this._zoomLimitedLayers.size) {
|
||||||
this._zoomLimitedLayers.forEach((layer) => {
|
this._zoomLimitedLayers.forEach((layer) => {
|
||||||
this._isLayerVisible(layer, zoom) ? this._map.addLayer(layer) : this._map.removeLayer(layer);
|
LiveAtlasLayerGroup._isLayerVisible(layer, zoom) ? this._addToMap(layer) : this._removeFromMap(layer);
|
||||||
});
|
});
|
||||||
//Nothing is zoom limited, but we've just been added to the map
|
//Nothing is zoom limited, but we've just been added to the map
|
||||||
} else if(onAdd) {
|
} else if(onAdd) {
|
||||||
this.eachLayer(this._map.addLayer, this._map);
|
this.eachLayer((layer: Layer) => this._addToMap(layer), this._map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Returns if this layer group has zoom limits defined
|
//Returns if this layer group has zoom limits defined
|
||||||
_isZoomLimited() {
|
private _isZoomLimited() {
|
||||||
return this.options.maxZoom !== undefined || this.options.minZoom !== undefined;
|
return this.options.maxZoom !== undefined || this.options.minZoom !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Returns if the given layer has its own zoom limits defined
|
//Returns if the given layer has its own zoom limits defined
|
||||||
_isLayerZoomLimited(layer: Layer) {
|
private static _isLayerZoomLimited(layer: Layer) {
|
||||||
return ((layer as any).options && (layer as any).options.minZoom !== undefined)
|
return ((layer as any).options && (layer as any).options.minZoom !== undefined)
|
||||||
&& ((layer as any).options && (layer as any).options.maxZoom !== undefined);
|
&& ((layer as any).options && (layer as any).options.maxZoom !== undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
_isLayerVisible(layer: Layer, currentZoom: number) {
|
private static _isLayerVisible(layer: Layer, currentZoom: number) {
|
||||||
let minZoom = -Infinity,
|
let minZoom = -Infinity,
|
||||||
maxZoom = Infinity;
|
maxZoom = Infinity;
|
||||||
|
|
||||||
@ -183,4 +196,17 @@ export default class LiveAtlasLayerGroup extends LayerGroup {
|
|||||||
|
|
||||||
return currentZoom >= minZoom && currentZoom <= maxZoom;
|
return currentZoom >= minZoom && currentZoom <= maxZoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _addToMap(layer: Layer) {
|
||||||
|
this._map.addLayer(layer)
|
||||||
|
|
||||||
|
//Create marker label immediately if labels are visible by default
|
||||||
|
if(layer instanceof GenericMarker && this.options.showLabels) {
|
||||||
|
(layer as GenericMarker).createLabel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _removeFromMap(layer: Layer) {
|
||||||
|
this._map.removeLayer(layer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,44 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {MarkerOptions, Marker, Util, LatLngExpression, Icon} from 'leaflet';
|
import {MarkerOptions, Marker, Util, LatLngExpression, Icon} from 'leaflet';
|
||||||
|
import {LiveAtlasMarker} from "@/index";
|
||||||
|
import {GenericIcon} from "@/leaflet/icon/GenericIcon";
|
||||||
|
|
||||||
export interface GenericMarkerOptions extends MarkerOptions {
|
export interface GenericMarkerOptions extends MarkerOptions {
|
||||||
|
icon: GenericIcon;
|
||||||
minZoom?: number;
|
minZoom?: number;
|
||||||
maxZoom?: number;
|
maxZoom?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GenericMarker extends Marker {
|
export class GenericMarker extends Marker {
|
||||||
constructor(latLng: LatLngExpression, options: GenericMarkerOptions) {
|
declare options: GenericMarkerOptions;
|
||||||
super(latLng, options);
|
|
||||||
Util.setOptions(this, options);
|
constructor(latLng: LatLngExpression, options: LiveAtlasMarker) {
|
||||||
|
super(latLng, {});
|
||||||
|
|
||||||
|
this.options.icon = new GenericIcon({
|
||||||
|
icon: options.icon,
|
||||||
|
label: options.label,
|
||||||
|
iconSize: options.dimensions,
|
||||||
|
isHtml: options.isLabelHTML,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.options.maxZoom = options.maxZoom;
|
||||||
|
this.options.minZoom = options.maxZoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
// noinspection JSUnusedGlobalSymbols
|
// noinspection JSUnusedGlobalSymbols
|
||||||
@ -35,4 +49,8 @@ export class GenericMarker extends Marker {
|
|||||||
getIcon(): Icon.Default {
|
getIcon(): Icon.Default {
|
||||||
return this.options.icon as Icon.Default;
|
return this.options.icon as Icon.Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createLabel(): void {
|
||||||
|
this.options.icon.createLabel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {LatLng, MarkerOptions, Marker, Util} from 'leaflet';
|
import {LatLng, MarkerOptions, Marker, Util} from 'leaflet';
|
||||||
import {DynmapPlayer} from "@/dynmap";
|
|
||||||
import {PlayerIcon} from "@/leaflet/icon/PlayerIcon";
|
import {PlayerIcon} from "@/leaflet/icon/PlayerIcon";
|
||||||
|
import {LiveAtlasPlayer} from "@/index";
|
||||||
|
|
||||||
export interface PlayerMarkerOptions extends MarkerOptions {
|
export interface PlayerMarkerOptions extends MarkerOptions {
|
||||||
smallFace: boolean,
|
smallFace: boolean,
|
||||||
@ -26,9 +26,9 @@ export interface PlayerMarkerOptions extends MarkerOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PlayerMarker extends Marker {
|
export class PlayerMarker extends Marker {
|
||||||
private _player: DynmapPlayer;
|
private _player: LiveAtlasPlayer;
|
||||||
|
|
||||||
constructor(player: DynmapPlayer, options: PlayerMarkerOptions) {
|
constructor(player: LiveAtlasPlayer, options: PlayerMarkerOptions) {
|
||||||
super(new LatLng(0, 0), options);
|
super(new LatLng(0, 0), options);
|
||||||
this._player = player;
|
this._player = player;
|
||||||
options.draggable = false;
|
options.draggable = false;
|
||||||
|
@ -1,46 +1,30 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
||||||
* These portions are Copyright 2020 Dynmap Contributors.
|
* These portions are Copyright 2020 Dynmap Contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TileLayer, Coords, DoneCallback, TileLayerOptions, DomUtil, Util, LatLng} from 'leaflet';
|
import {Coords, DoneCallback, DomUtil} from 'leaflet';
|
||||||
import {store} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import {Coordinate} from "@/index";
|
import {Coordinate} from "@/index";
|
||||||
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
import {LiveAtlasTileLayerOptions, LiveAtlasTileLayer} from "@/leaflet/tileLayer/LiveAtlasTileLayer";
|
||||||
|
import {computed, watch} from "@vue/runtime-core";
|
||||||
export interface DynmapTileLayerOptions extends TileLayerOptions {
|
import {ComputedRef} from "@vue/reactivity";
|
||||||
mapSettings: LiveAtlasMapDefinition;
|
import {WatchStopHandle} from "vue";
|
||||||
errorTileUrl: string;
|
import {ActionTypes} from "@/store/action-types";
|
||||||
night?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DynmapTileLayer extends TileLayer {
|
|
||||||
options: DynmapTileLayerOptions;
|
|
||||||
_mapSettings: LiveAtlasMapDefinition;
|
|
||||||
_cachedTileUrls: Map<string, string>;
|
|
||||||
_namedTiles: Map<string, DynmapTileElement>;
|
|
||||||
_tileTemplate: DynmapTileElement;
|
|
||||||
_loadQueue: DynmapTileElement[];
|
|
||||||
_loadingTiles: Set<DynmapTileElement>;
|
|
||||||
|
|
||||||
locationToLatLng(location: Coordinate): LatLng;
|
|
||||||
|
|
||||||
latLngToLocation(latLng: LatLng): Coordinate;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DynmapTile {
|
export interface DynmapTile {
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
@ -68,43 +52,59 @@ export interface TileInfo {
|
|||||||
fmt: string;
|
fmt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
|
||||||
// noinspection JSUnusedGlobalSymbols
|
// noinspection JSUnusedGlobalSymbols
|
||||||
export class DynmapTileLayer extends TileLayer {
|
export class DynmapTileLayer extends LiveAtlasTileLayer {
|
||||||
constructor(options: DynmapTileLayerOptions) {
|
private readonly _cachedTileUrls: Map<any, any> = Object.seal(new Map());
|
||||||
|
private readonly _namedTiles: Map<any, any> = Object.seal(new Map());
|
||||||
|
private readonly _loadQueue: DynmapTileElement[] = [];
|
||||||
|
private readonly _loadingTiles: Set<DynmapTileElement> = Object.seal(new Set());
|
||||||
|
private readonly _tileTemplate: DynmapTileElement;
|
||||||
|
private readonly _baseUrl: string;
|
||||||
|
|
||||||
|
private readonly _night: ComputedRef<boolean>;
|
||||||
|
private readonly _pendingUpdates: ComputedRef<boolean>;
|
||||||
|
private readonly _nightUnwatch: WatchStopHandle;
|
||||||
|
private readonly _updateUnwatch: WatchStopHandle;
|
||||||
|
private _updateFrame: number = 0;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
declare options: DynmapTileLayerOptions;
|
||||||
|
|
||||||
|
constructor(options: LiveAtlasTileLayerOptions) {
|
||||||
super('', options);
|
super('', options);
|
||||||
|
|
||||||
this._mapSettings = options.mapSettings;
|
this._mapSettings = options.mapSettings;
|
||||||
options.maxZoom = this._mapSettings.nativeZoomLevels + this._mapSettings.extraZoomLevels;
|
|
||||||
options.maxNativeZoom = this._mapSettings.nativeZoomLevels;
|
|
||||||
options.zoomReverse = true;
|
|
||||||
options.tileSize = 128;
|
|
||||||
options.minZoom = 0;
|
|
||||||
|
|
||||||
Util.setOptions(this, options);
|
|
||||||
|
|
||||||
if (options.mapSettings === null) {
|
|
||||||
throw new TypeError("mapSettings missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
this._cachedTileUrls = Object.seal(new Map());
|
|
||||||
this._namedTiles = Object.seal(new Map());
|
|
||||||
this._loadQueue = [];
|
|
||||||
this._loadingTiles = Object.seal(new Set());
|
|
||||||
|
|
||||||
this._tileTemplate = DomUtil.create('img', 'leaflet-tile') as DynmapTileElement;
|
this._tileTemplate = DomUtil.create('img', 'leaflet-tile') as DynmapTileElement;
|
||||||
this._tileTemplate.style.width = this._tileTemplate.style.height = this.options.tileSize + 'px';
|
this._tileTemplate.style.width = this._tileTemplate.style.height = this.options.tileSize + 'px';
|
||||||
this._tileTemplate.alt = '';
|
this._tileTemplate.alt = '';
|
||||||
this._tileTemplate.tileName = '';
|
this._tileTemplate.tileName = '';
|
||||||
this._tileTemplate.setAttribute('role', 'presentation');
|
this._tileTemplate.setAttribute('role', 'presentation');
|
||||||
|
this._baseUrl = store.state.currentMapProvider!.getTilesUrl();
|
||||||
|
|
||||||
Object.seal(this._tileTemplate);
|
Object.seal(this._tileTemplate);
|
||||||
|
|
||||||
if(this.options.crossOrigin || this.options.crossOrigin === '') {
|
if(this.options.crossOrigin || this.options.crossOrigin === '') {
|
||||||
this._tileTemplate.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
|
this._tileTemplate.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._pendingUpdates = computed(() => !!store.state.pendingTileUpdates.length);
|
||||||
|
this._updateUnwatch = watch(this._pendingUpdates, (newValue, oldValue) => {
|
||||||
|
if(newValue && !oldValue && !this._updateFrame) {
|
||||||
|
this.handlePendingUpdates();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._night = computed(() => store.getters.night);
|
||||||
|
this._nightUnwatch = watch(this._night, () => {
|
||||||
|
if(this._mapSettings.nightAndDay) {
|
||||||
|
this.redraw();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getTileName(coords: Coordinate) {
|
private getTileName(coords: Coordinate) {
|
||||||
const info = this.getTileInfo(coords);
|
const info = this.getTileInfo(coords);
|
||||||
// Y is inverted for HD-map.
|
// Y is inverted for HD-map.
|
||||||
info.y = -info.y;
|
info.y = -info.y;
|
||||||
@ -116,12 +116,12 @@ export class DynmapTileLayer extends TileLayer {
|
|||||||
return this.getTileUrlFromName(this.getTileName(coords));
|
return this.getTileUrlFromName(this.getTileName(coords));
|
||||||
}
|
}
|
||||||
|
|
||||||
getTileUrlFromName(name: string, timestamp?: number) {
|
private getTileUrlFromName(name: string, timestamp?: number) {
|
||||||
let url = this._cachedTileUrls.get(name);
|
let url = this._cachedTileUrls.get(name);
|
||||||
|
|
||||||
if (!url) {
|
if (!url) {
|
||||||
const path = escape(`${this._mapSettings.world.name}/${name}`);
|
const path = escape(`${this._mapSettings.world.name}/${name}`);
|
||||||
url = `${store.getters.serverConfig.dynmap.tiles}${path}`;
|
url = `${this._baseUrl}${path}`;
|
||||||
|
|
||||||
if(typeof timestamp !== 'undefined') {
|
if(typeof timestamp !== 'undefined') {
|
||||||
url += (url.indexOf('?') === -1 ? `?timestamp=${timestamp}` : `×tamp=${timestamp}`);
|
url += (url.indexOf('?') === -1 ? `?timestamp=${timestamp}` : `×tamp=${timestamp}`);
|
||||||
@ -133,7 +133,7 @@ export class DynmapTileLayer extends TileLayer {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateNamedTile(name: string, timestamp: number) {
|
private updateNamedTile(name: string, timestamp: number) {
|
||||||
const tile = this._namedTiles.get(name);
|
const tile = this._namedTiles.get(name);
|
||||||
this._cachedTileUrls.delete(name);
|
this._cachedTileUrls.delete(name);
|
||||||
|
|
||||||
@ -240,14 +240,14 @@ export class DynmapTileLayer extends TileLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Some helper functions.
|
// Some helper functions.
|
||||||
zoomprefix(amount: number) {
|
private zoomprefix(amount: number) {
|
||||||
// amount == 0 -> ''
|
// amount == 0 -> ''
|
||||||
// amount == 1 -> 'z_'
|
// amount == 1 -> 'z_'
|
||||||
// amount == 2 -> 'zz_'
|
// amount == 2 -> 'zz_'
|
||||||
return 'z'.repeat(amount) + (amount === 0 ? '' : '_');
|
return 'z'.repeat(amount) + (amount === 0 ? '' : '_');
|
||||||
}
|
}
|
||||||
|
|
||||||
getTileInfo(coords: Coordinate): TileInfo {
|
private getTileInfo(coords: Coordinate): TileInfo {
|
||||||
// zoom: max zoomed in = this.options.maxZoom, max zoomed out = 0
|
// zoom: max zoomed in = this.options.maxZoom, max zoomed out = 0
|
||||||
// izoom: max zoomed in = 0, max zoomed out = this.options.maxZoom
|
// izoom: max zoomed in = 0, max zoomed out = this.options.maxZoom
|
||||||
// zoomoutlevel: izoom < mapzoomin -> 0, else -> izoom - mapzoomin (which ranges from 0 till mapzoomout)
|
// zoomoutlevel: izoom < mapzoomin -> 0, else -> izoom - mapzoomin (which ranges from 0 till mapzoomout)
|
||||||
@ -259,7 +259,7 @@ export class DynmapTileLayer extends TileLayer {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
prefix: this._mapSettings.prefix,
|
prefix: this._mapSettings.prefix,
|
||||||
nightday: (this._mapSettings.nightAndDay && !this.options.night) ? '_day' : '',
|
nightday: (this._mapSettings.nightAndDay && !this._night.value) ? '_day' : '',
|
||||||
scaledx: x >> 5,
|
scaledx: x >> 5,
|
||||||
scaledy: y >> 5,
|
scaledy: y >> 5,
|
||||||
zoom: this.zoomprefix(zoomoutlevel),
|
zoom: this.zoomprefix(zoomoutlevel),
|
||||||
@ -270,10 +270,34 @@ export class DynmapTileLayer extends TileLayer {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
setNight(night: boolean) {
|
private async handlePendingUpdates() {
|
||||||
if(this.options.night !== night) {
|
const updates = await store.dispatch(ActionTypes.POP_TILE_UPDATES, 10);
|
||||||
this.options.night = night;
|
|
||||||
this.redraw();
|
for(const update of updates) {
|
||||||
|
this.updateNamedTile(update.name, update.timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this._pendingUpdates.value) {
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
this._updateFrame = requestAnimationFrame(() => this.handlePendingUpdates());
|
||||||
|
} else {
|
||||||
|
this._updateFrame = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
super.remove();
|
||||||
|
|
||||||
|
this._nightUnwatch();
|
||||||
|
|
||||||
|
if(this._updateFrame) {
|
||||||
|
cancelAnimationFrame(this._updateFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this._updateUnwatch) {
|
||||||
|
this._updateUnwatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
46
src/leaflet/tileLayer/LiveAtlasTileLayer.ts
Normal file
46
src/leaflet/tileLayer/LiveAtlasTileLayer.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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 {TileLayer, TileLayerOptions, Util} from 'leaflet';
|
||||||
|
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
||||||
|
|
||||||
|
export interface LiveAtlasTileLayerOptions extends TileLayerOptions {
|
||||||
|
mapSettings: LiveAtlasMapDefinition;
|
||||||
|
errorTileUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
export abstract class LiveAtlasTileLayer extends TileLayer {
|
||||||
|
protected _mapSettings: LiveAtlasMapDefinition;
|
||||||
|
declare options: LiveAtlasTileLayerOptions;
|
||||||
|
|
||||||
|
protected constructor(url: string, options: LiveAtlasTileLayerOptions) {
|
||||||
|
super(url, options);
|
||||||
|
|
||||||
|
this._mapSettings = options.mapSettings;
|
||||||
|
options.maxZoom = this._mapSettings.nativeZoomLevels + this._mapSettings.extraZoomLevels;
|
||||||
|
options.maxNativeZoom = this._mapSettings.nativeZoomLevels;
|
||||||
|
options.zoomReverse = true;
|
||||||
|
options.tileSize = 128;
|
||||||
|
options.minZoom = 0;
|
||||||
|
|
||||||
|
Util.setOptions(this, options);
|
||||||
|
|
||||||
|
if (options.mapSettings === null) {
|
||||||
|
throw new TypeError("mapSettings missing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
src/leaflet/tileLayer/Pl3xmapTileLayer.ts
Normal file
34
src/leaflet/tileLayer/Pl3xmapTileLayer.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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 {LiveAtlasTileLayer, LiveAtlasTileLayerOptions} from "@/leaflet/tileLayer/LiveAtlasTileLayer";
|
||||||
|
import {useStore} from "@/store";
|
||||||
|
import {Util} from "leaflet";
|
||||||
|
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
export class Pl3xmapTileLayer extends LiveAtlasTileLayer {
|
||||||
|
constructor(options: LiveAtlasTileLayerOptions) {
|
||||||
|
const worldName = options.mapSettings.world.name,
|
||||||
|
baseUrl = useStore().state.currentMapProvider!.getTilesUrl();
|
||||||
|
|
||||||
|
super(`${baseUrl}${worldName}/{z}/{x}_{y}.png`, options);
|
||||||
|
|
||||||
|
options.tileSize = 512;
|
||||||
|
options.zoomReverse = false;
|
||||||
|
|
||||||
|
Util.setOptions(this, options);
|
||||||
|
}
|
||||||
|
}
|
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 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 {LatLngExpression, Polygon, PolylineOptions, Util} from "leaflet";
|
|
||||||
|
|
||||||
export interface LiveAtlasPolygonOptions extends PolylineOptions {
|
|
||||||
minZoom?: number;
|
|
||||||
maxZoom?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class LiveAtlasPolygon extends Polygon {
|
|
||||||
constructor(latlngs: LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][], options?: LiveAtlasPolygonOptions) {
|
|
||||||
super(latlngs, options);
|
|
||||||
Util.setOptions(this, options);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 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 {LatLngExpression, Polyline, PolylineOptions, Util} from "leaflet";
|
|
||||||
|
|
||||||
export interface LiveAtlasPolylineOptions extends PolylineOptions {
|
|
||||||
minZoom?: number;
|
|
||||||
maxZoom?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class LiveAtlasPolyline extends Polyline {
|
|
||||||
constructor(latlngs: LatLngExpression[] | LatLngExpression[][], options?: LiveAtlasPolylineOptions) {
|
|
||||||
super(latlngs, options);
|
|
||||||
Util.setOptions(this, options);
|
|
||||||
}
|
|
||||||
}
|
|
20
src/main.ts
20
src/main.ts
@ -1,17 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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 {Coordinate, LiveAtlasWorldDefinition} from "@/index";
|
import {Coordinate, LiveAtlasWorldDefinition} from "@/index";
|
||||||
import {LatLng} from "leaflet";
|
import {LatLng} from "leaflet";
|
||||||
import {LiveAtlasProjection} from "@/model/LiveAtlasProjection";
|
import {LiveAtlasProjection} from "@/model/LiveAtlasProjection";
|
||||||
@ -5,8 +21,8 @@ import {LiveAtlasProjection} from "@/model/LiveAtlasProjection";
|
|||||||
export interface LiveAtlasMapDefinitionOptions {
|
export interface LiveAtlasMapDefinitionOptions {
|
||||||
world: LiveAtlasWorldDefinition;
|
world: LiveAtlasWorldDefinition;
|
||||||
name: string;
|
name: string;
|
||||||
|
displayName?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
title?: string;
|
|
||||||
background?: string;
|
background?: string;
|
||||||
nightAndDay?: boolean;
|
nightAndDay?: boolean;
|
||||||
backgroundDay?: string;
|
backgroundDay?: string;
|
||||||
@ -24,7 +40,7 @@ export default class LiveAtlasMapDefinition {
|
|||||||
readonly world: LiveAtlasWorldDefinition;
|
readonly world: LiveAtlasWorldDefinition;
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly icon?: string;
|
readonly icon?: string;
|
||||||
readonly title: string;
|
readonly displayName: string;
|
||||||
readonly background: string;
|
readonly background: string;
|
||||||
readonly nightAndDay: boolean;
|
readonly nightAndDay: boolean;
|
||||||
readonly backgroundDay?: string;
|
readonly backgroundDay?: string;
|
||||||
@ -35,12 +51,13 @@ export default class LiveAtlasMapDefinition {
|
|||||||
private readonly projection?: Readonly<LiveAtlasProjection>;
|
private readonly projection?: Readonly<LiveAtlasProjection>;
|
||||||
readonly nativeZoomLevels: number;
|
readonly nativeZoomLevels: number;
|
||||||
readonly extraZoomLevels: number;
|
readonly extraZoomLevels: number;
|
||||||
|
readonly scale: number;
|
||||||
|
|
||||||
constructor(options: LiveAtlasMapDefinitionOptions) {
|
constructor(options: LiveAtlasMapDefinitionOptions) {
|
||||||
this.world = options.world; //Ignore append_to_world here otherwise things break
|
this.world = options.world; //Ignore append_to_world here otherwise things break
|
||||||
this.name = options.name;
|
this.name = options.name;
|
||||||
this.icon = options.icon || undefined;
|
this.icon = options.icon || undefined;
|
||||||
this.title = options.title || '';
|
this.displayName = options.displayName || '';
|
||||||
|
|
||||||
this.background = options.background || '#000000';
|
this.background = options.background || '#000000';
|
||||||
this.nightAndDay = options.nightAndDay || false;
|
this.nightAndDay = options.nightAndDay || false;
|
||||||
@ -53,24 +70,25 @@ export default class LiveAtlasMapDefinition {
|
|||||||
|
|
||||||
this.nativeZoomLevels = options.nativeZoomLevels || 1;
|
this.nativeZoomLevels = options.nativeZoomLevels || 1;
|
||||||
this.extraZoomLevels = options.extraZoomLevels || 0;
|
this.extraZoomLevels = options.extraZoomLevels || 0;
|
||||||
|
this.scale = (1 / Math.pow(2, this.nativeZoomLevels));
|
||||||
|
|
||||||
if(options.mapToWorld || options.worldToMap) {
|
if(options.mapToWorld || options.worldToMap) {
|
||||||
this.projection = Object.freeze(new LiveAtlasProjection({
|
this.projection = new LiveAtlasProjection({
|
||||||
mapToWorld: options.mapToWorld || [0, 0, 0, 0, 0, 0, 0, 0, 0],
|
mapToWorld: options.mapToWorld || [0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
worldToMap: options.worldToMap || [0, 0, 0, 0, 0, 0, 0, 0, 0],
|
worldToMap: options.worldToMap || [0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
nativeZoomLevels: this.nativeZoomLevels,
|
nativeZoomLevels: this.nativeZoomLevels,
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
locationToLatLng(location: Coordinate): LatLng {
|
locationToLatLng(location: Coordinate): LatLng {
|
||||||
return this.projection ? this.projection.locationToLatLng(location)
|
return this.projection ? this.projection.locationToLatLng(location)
|
||||||
: LiveAtlasMapDefinition.defaultProjection.locationToLatLng(location);
|
: new LatLng(-location.z * this.scale, location.x * this.scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
latLngToLocation(latLng: LatLng, y: number): Coordinate {
|
latLngToLocation(latLng: LatLng, y: number): Coordinate {
|
||||||
return this.projection ? this.projection.latLngToLocation(latLng, y)
|
return this.projection ? this.projection.latLngToLocation(latLng, y)
|
||||||
: LiveAtlasMapDefinition.defaultProjection.latLngToLocation(latLng, y);
|
: {x: latLng.lng / this.scale, y: y, z: -latLng.lat / this.scale};
|
||||||
}
|
}
|
||||||
|
|
||||||
getIcon(): string {
|
getIcon(): string {
|
||||||
@ -97,14 +115,4 @@ export default class LiveAtlasMapDefinition {
|
|||||||
|
|
||||||
return `block_${worldType}_${mapType}`;
|
return `block_${worldType}_${mapType}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProjection = Object.freeze({
|
|
||||||
locationToLatLng(location: Coordinate): LatLng {
|
|
||||||
return new LatLng(location.x, location.z);
|
|
||||||
},
|
|
||||||
|
|
||||||
latLngToLocation(latLng: LatLng, y: number): Coordinate {
|
|
||||||
return {x: latLng.lat, y: y, z: latLng.lng};
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 James Lyne
|
|
||||||
*
|
|
||||||
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
|
||||||
* These portions are Copyright 2020 Dynmap Contributors.
|
|
||||||
*
|
|
||||||
* 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 {LatLng} from 'leaflet';
|
|
||||||
import {Coordinate} from "@/index";
|
|
||||||
|
|
||||||
export interface LiveAtlasProjectionOptions {
|
|
||||||
mapToWorld: [number, number, number, number, number, number, number, number, number],
|
|
||||||
worldToMap: [number, number, number, number, number, number, number, number, number],
|
|
||||||
nativeZoomLevels: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export class LiveAtlasProjection {
|
|
||||||
private readonly options: LiveAtlasProjectionOptions
|
|
||||||
|
|
||||||
constructor(options: LiveAtlasProjectionOptions) {
|
|
||||||
this.options = {
|
|
||||||
mapToWorld: options.mapToWorld || [0, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
worldToMap: options.worldToMap || [0, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
nativeZoomLevels: options.nativeZoomLevels || 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
locationToLatLng(location: Coordinate): LatLng {
|
|
||||||
const wtp = this.options.worldToMap,
|
|
||||||
lat = wtp[3] * location.x + wtp[4] * location.y + wtp[5] * location.z,
|
|
||||||
lng = wtp[0] * location.x + wtp[1] * location.y + wtp[2] * location.z;
|
|
||||||
|
|
||||||
return new LatLng(
|
|
||||||
-((128 - lat) / (1 << this.options.nativeZoomLevels)),
|
|
||||||
lng / (1 << this.options.nativeZoomLevels));
|
|
||||||
}
|
|
||||||
|
|
||||||
latLngToLocation(latLng: LatLng, y: number): Coordinate {
|
|
||||||
const ptw = this.options.mapToWorld,
|
|
||||||
lat = latLng.lng * (1 << this.options.nativeZoomLevels),
|
|
||||||
lon = 128 + latLng.lat * (1 << this.options.nativeZoomLevels),
|
|
||||||
x = ptw[0] * lat + ptw[1] * lon + ptw[2] * y,
|
|
||||||
z = ptw[6] * lat + ptw[7] * lon + ptw[8] * y;
|
|
||||||
|
|
||||||
return {x: x, y: y, z: z};
|
|
||||||
}
|
|
||||||
}
|
|
821
src/providers/DynmapMapProvider.ts
Normal file
821
src/providers/DynmapMapProvider.ts
Normal file
@ -0,0 +1,821 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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 {
|
||||||
|
HeadQueueEntry,
|
||||||
|
LiveAtlasArea, LiveAtlasChat,
|
||||||
|
LiveAtlasCircle, LiveAtlasComponentConfig,
|
||||||
|
LiveAtlasDimension,
|
||||||
|
LiveAtlasLine,
|
||||||
|
LiveAtlasMarker,
|
||||||
|
LiveAtlasMarkerSet,
|
||||||
|
LiveAtlasPlayer, LiveAtlasServerConfig,
|
||||||
|
LiveAtlasServerDefinition,
|
||||||
|
LiveAtlasServerMessageConfig,
|
||||||
|
LiveAtlasWorldDefinition
|
||||||
|
} from "@/index";
|
||||||
|
import {
|
||||||
|
DynmapMarkerSetUpdates, DynmapTileUpdate, DynmapUpdate
|
||||||
|
} from "@/dynmap";
|
||||||
|
import {useStore} from "@/store";
|
||||||
|
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
||||||
|
import ChatError from "@/errors/ChatError";
|
||||||
|
import {MutationTypes} from "@/store/mutation-types";
|
||||||
|
import MapProvider from "@/providers/MapProvider";
|
||||||
|
import {ActionTypes} from "@/store/action-types";
|
||||||
|
import {endWorldNameRegex, netherWorldNameRegex, titleColoursRegex} from "@/util";
|
||||||
|
import {getPoints} from "@/util/areas";
|
||||||
|
import {getLinePoints} from "@/util/lines";
|
||||||
|
|
||||||
|
export default class DynmapMapProvider extends MapProvider {
|
||||||
|
private configurationAbort?: AbortController = undefined;
|
||||||
|
private markersAbort?: AbortController = undefined;
|
||||||
|
private updateAbort?: AbortController = undefined;
|
||||||
|
|
||||||
|
private updatesEnabled = false;
|
||||||
|
private updateTimeout: number = 0;
|
||||||
|
private updateTimestamp: Date = new Date();
|
||||||
|
private updateInterval: number = 3000;
|
||||||
|
|
||||||
|
constructor(config: LiveAtlasServerDefinition) {
|
||||||
|
super(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildServerConfig(response: any): LiveAtlasServerConfig {
|
||||||
|
return {
|
||||||
|
defaultMap: response.defaultmap || undefined,
|
||||||
|
defaultWorld: response.defaultworld || undefined,
|
||||||
|
defaultZoom: response.defaultzoom || 0,
|
||||||
|
followMap: response.followmap || undefined,
|
||||||
|
followZoom: response.followzoom,
|
||||||
|
title: response.title.replace(titleColoursRegex, '') || 'Dynmap',
|
||||||
|
expandUI: response.sidebaropened && response.sidebaropened !== 'false', //Sent as a string for some reason
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildMessagesConfig(response: any): LiveAtlasServerMessageConfig {
|
||||||
|
return {
|
||||||
|
chatPlayerJoin: response.joinmessage || '',
|
||||||
|
chatPlayerQuit: response.quitmessage || '',
|
||||||
|
chatAnonymousJoin: response['msg-hiddennamejoin'] || '',
|
||||||
|
chatAnonymousQuit: response['msg-hiddennamequit'] || '',
|
||||||
|
chatErrorNotAllowed: response['msg-chatnotallowed'] || '',
|
||||||
|
chatErrorRequiresLogin: response['msg-chatrequireslogin'] || '',
|
||||||
|
chatErrorCooldown: response.spammessage || '',
|
||||||
|
worldsHeading: response['msg-maptypes'] || '',
|
||||||
|
playersHeading: response['msg-players'] ? `${response['msg-players']} ({cur}/{max})` : '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildWorlds(response: any): Array<LiveAtlasWorldDefinition> {
|
||||||
|
const worlds: Map<string, LiveAtlasWorldDefinition> = new Map<string, LiveAtlasWorldDefinition>();
|
||||||
|
|
||||||
|
//Get all the worlds first so we can handle append_to_world properly
|
||||||
|
(response.worlds || []).forEach((world: any) => {
|
||||||
|
let worldType: LiveAtlasDimension = 'overworld';
|
||||||
|
|
||||||
|
if (netherWorldNameRegex.test(world.name) || (world.name == 'DIM-1')) {
|
||||||
|
worldType = 'nether';
|
||||||
|
} else if (endWorldNameRegex.test(world.name) || (world.name == 'DIM1')) {
|
||||||
|
worldType = 'end';
|
||||||
|
}
|
||||||
|
|
||||||
|
worlds.set(world.name, {
|
||||||
|
name: world.name,
|
||||||
|
displayName: world.title || '',
|
||||||
|
dimension: worldType,
|
||||||
|
protected: world.protected || false,
|
||||||
|
height: world.height || 256,
|
||||||
|
seaLevel: world.sealevel || 64,
|
||||||
|
center: {
|
||||||
|
x: world.center.x || 0,
|
||||||
|
y: world.center.y || 0,
|
||||||
|
z: world.center.z || 0
|
||||||
|
},
|
||||||
|
maps: new Map(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
(response.worlds || []).forEach((world: any) => {
|
||||||
|
(world.maps || []).forEach((map: any) => {
|
||||||
|
const worldName = map.append_to_world || world.name,
|
||||||
|
w = worlds.get(worldName);
|
||||||
|
|
||||||
|
if(!w) {
|
||||||
|
console.warn(`Ignoring map '${map.name}' associated with non-existent world '${worldName}'`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
w.maps.set(map.name, Object.freeze(new LiveAtlasMapDefinition({
|
||||||
|
world: w, //Ignore append_to_world here otherwise things break
|
||||||
|
background: map.background || '#000000',
|
||||||
|
backgroundDay: map.backgroundday || '#000000',
|
||||||
|
backgroundNight: map.backgroundnight || '#000000',
|
||||||
|
icon: map.icon || undefined,
|
||||||
|
imageFormat: map['image-format'] || 'png',
|
||||||
|
name: map.name || '(Unnamed map)',
|
||||||
|
nightAndDay: map.nightandday || false,
|
||||||
|
prefix: map.prefix || '',
|
||||||
|
protected: map.protected || false,
|
||||||
|
displayName: map.title || '',
|
||||||
|
mapToWorld: map.maptoworld || undefined,
|
||||||
|
worldToMap: map.worldtomap || undefined,
|
||||||
|
nativeZoomLevels: map.mapzoomout || 1,
|
||||||
|
extraZoomLevels: map.mapzoomin || 0
|
||||||
|
})));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return Array.from(worlds.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildComponents(response: any): LiveAtlasComponentConfig {
|
||||||
|
const components: LiveAtlasComponentConfig = {
|
||||||
|
markers: {
|
||||||
|
showLabels: false,
|
||||||
|
},
|
||||||
|
chatBox: undefined,
|
||||||
|
chatBalloons: false,
|
||||||
|
playerMarkers: undefined,
|
||||||
|
coordinatesControl: undefined,
|
||||||
|
layerControl: response.showlayercontrol && response.showlayercontrol !== 'false', //Sent as a string for some reason
|
||||||
|
linkControl: false,
|
||||||
|
clockControl: undefined,
|
||||||
|
logoControls: [],
|
||||||
|
login: response['login-enabled'] || false,
|
||||||
|
};
|
||||||
|
|
||||||
|
(response.components || []).forEach((component: any) => {
|
||||||
|
const type = component.type || "unknown";
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "markers":
|
||||||
|
components.markers = {
|
||||||
|
showLabels: component.showlabel || false,
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "playermarkers":
|
||||||
|
components.playerMarkers = {
|
||||||
|
grayHiddenPlayers: response.grayplayerswhenhidden || false,
|
||||||
|
hideByDefault: component.hidebydefault || false,
|
||||||
|
layerName: component.label || "Players",
|
||||||
|
layerPriority: component.layerprio || 0,
|
||||||
|
showBodies: component.showplayerbody || false,
|
||||||
|
showSkinFaces: component.showplayerfaces || false,
|
||||||
|
showHealth: component.showplayerhealth || false,
|
||||||
|
smallFaces: component.smallplayerfaces || false,
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "coord":
|
||||||
|
components.coordinatesControl = {
|
||||||
|
showY: !(component.hidey || false),
|
||||||
|
label: component.label || "Location: ",
|
||||||
|
showRegion: component['show-mcr'] || false,
|
||||||
|
showChunk: component['show-chunk'] || false,
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "link":
|
||||||
|
components.linkControl = true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "digitalclock":
|
||||||
|
components.clockControl = {
|
||||||
|
showDigitalClock: true,
|
||||||
|
showWeather: false,
|
||||||
|
showTimeOfDay: false,
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "timeofdayclock":
|
||||||
|
components.clockControl = {
|
||||||
|
showTimeOfDay: true,
|
||||||
|
showDigitalClock: component.showdigitalclock || false,
|
||||||
|
showWeather: component.showweather || false,
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "logo":
|
||||||
|
components.logoControls.push({
|
||||||
|
text: component.text || '',
|
||||||
|
url: component.linkurl || undefined,
|
||||||
|
position: component.position.replace('-', '') || 'topleft',
|
||||||
|
image: component.logourl || undefined,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "chat":
|
||||||
|
if (response.allowwebchat) {
|
||||||
|
components.chatSending = {
|
||||||
|
loginRequired: response['webchat-requires-login'] || false,
|
||||||
|
maxLength: response['chatlengthlimit'] || 256,
|
||||||
|
cooldown: response['webchat-interval'] || 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "chatbox":
|
||||||
|
components.chatBox = {
|
||||||
|
allowUrlName: component.allowurlname || false,
|
||||||
|
showPlayerFaces: component.showplayerfaces || false,
|
||||||
|
messageLifetime: component.messagettl || Infinity,
|
||||||
|
messageHistory: component.scrollback || Infinity,
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "chatballoon":
|
||||||
|
components.chatBalloons = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildMarkerSet(id: string, data: any): any {
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
label: data.label || "Unnamed set",
|
||||||
|
hidden: data.hide || false,
|
||||||
|
priority: data.layerprio || 0,
|
||||||
|
showLabels: data.showlabels || undefined,
|
||||||
|
minZoom: typeof data.minzoom !== 'undefined' && data.minzoom > -1 ? data.minzoom : undefined,
|
||||||
|
maxZoom: typeof data.maxzoom !== 'undefined' && data.maxzoom > -1 ? data.maxzoom : undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildMarkers(data: any): Map<string, LiveAtlasMarker> {
|
||||||
|
const markers = Object.freeze(new Map()) as Map<string, LiveAtlasMarker>;
|
||||||
|
|
||||||
|
for (const key in data) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(data, key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
markers.set(key, DynmapMapProvider.buildMarker(data[key]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return markers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildMarker(marker: any): LiveAtlasMarker {
|
||||||
|
return {
|
||||||
|
label: marker.label || '',
|
||||||
|
isLabelHTML: marker.markup || false,
|
||||||
|
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",
|
||||||
|
minZoom: typeof marker.minzoom !== 'undefined' && marker.minzoom > -1 ? marker.minzoom : undefined,
|
||||||
|
maxZoom: typeof marker.maxzoom !== 'undefined' && marker.maxzoom > -1 ? marker.maxzoom : undefined,
|
||||||
|
popupContent: marker.desc || undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildAreas(data: any): Map<string, LiveAtlasArea> {
|
||||||
|
const areas = Object.freeze(new Map()) as Map<string, LiveAtlasArea>;
|
||||||
|
|
||||||
|
for (const key in data) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(data, key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
areas.set(key, DynmapMapProvider.buildArea(data[key]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return areas;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildArea(area: any): LiveAtlasArea {
|
||||||
|
const opacity = area.fillopacity || 0,
|
||||||
|
x = area.x || [0, 0],
|
||||||
|
y: [number, number] = [area.ybottom || 0, area.ytop || 0],
|
||||||
|
z = area.z || [0, 0];
|
||||||
|
|
||||||
|
return Object.seal({
|
||||||
|
style: {
|
||||||
|
color: area.color || '#ff0000',
|
||||||
|
opacity: area.opacity || 1,
|
||||||
|
weight: area.weight || 1,
|
||||||
|
fillColor: area.fillcolor || '#ff0000',
|
||||||
|
fillOpacity: area.fillopacity || 0,
|
||||||
|
},
|
||||||
|
outline: !opacity,
|
||||||
|
points: getPoints(x, y, z, !opacity),
|
||||||
|
minZoom: typeof area.minzoom !== 'undefined' && area.minzoom > -1 ? area.minzoom : undefined,
|
||||||
|
maxZoom: typeof area.maxzoom !== 'undefined' && area.maxzoom > -1 ? area.maxzoom : undefined,
|
||||||
|
|
||||||
|
isPopupHTML: area.desc ? true : area.markup || false,
|
||||||
|
popupContent: area.desc || area.label || undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildLines(data: any): Map<string, LiveAtlasLine> {
|
||||||
|
const lines = Object.freeze(new Map()) as Map<string, LiveAtlasLine>;
|
||||||
|
|
||||||
|
for (const key in data) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(data, key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.set(key, DynmapMapProvider.buildLine(data[key]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildLine(line: any): LiveAtlasLine {
|
||||||
|
return Object.seal({
|
||||||
|
style: {
|
||||||
|
color: line.color || '#ff0000',
|
||||||
|
opacity: line.opacity || 1,
|
||||||
|
weight: line.weight || 1,
|
||||||
|
},
|
||||||
|
points: getLinePoints(line.x || [0, 0], line.y || [0, 0], line.z || [0, 0]),
|
||||||
|
minZoom: typeof line.minzoom !== 'undefined' && line.minzoom > -1 ? line.minzoom : undefined,
|
||||||
|
maxZoom: typeof line.maxzoom !== 'undefined' && line.maxzoom > -1 ? line.maxzoom : undefined,
|
||||||
|
|
||||||
|
isPopupHTML: line.desc ? true : line.markup || false,
|
||||||
|
popupContent: line.desc || line.label || undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildCircles(data: any): Map<string, LiveAtlasCircle> {
|
||||||
|
const circles = Object.freeze(new Map()) as Map<string, LiveAtlasCircle>;
|
||||||
|
|
||||||
|
for (const key in data) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(data, key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
circles.set(key, DynmapMapProvider.buildCircle(data[key]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return circles;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildCircle(circle: any): LiveAtlasCircle {
|
||||||
|
return Object.seal({
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
minZoom: typeof circle.minzoom !== 'undefined' && circle.minzoom > -1 ? circle.minzoom : undefined,
|
||||||
|
maxZoom: typeof circle.maxzoom !== 'undefined' && circle.maxzoom > -1 ? circle.maxzoom : undefined,
|
||||||
|
|
||||||
|
isPopupHTML: circle.desc ? true : circle.markup || false,
|
||||||
|
popupContent: circle.desc || circle.label || undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildUpdates(data: Array<any>) {
|
||||||
|
const updates = {
|
||||||
|
markerSets: new Map<string, DynmapMarkerSetUpdates>(),
|
||||||
|
tiles: [] as DynmapTileUpdate[],
|
||||||
|
chat: [] as LiveAtlasChat[],
|
||||||
|
},
|
||||||
|
dropped = {
|
||||||
|
stale: 0,
|
||||||
|
noSet: 0,
|
||||||
|
noId: 0,
|
||||||
|
unknownType: 0,
|
||||||
|
unknownCType: 0,
|
||||||
|
incomplete: 0,
|
||||||
|
notImplemented: 0,
|
||||||
|
},
|
||||||
|
lastUpdate = this.updateTimestamp;
|
||||||
|
|
||||||
|
let accepted = 0;
|
||||||
|
|
||||||
|
for (const entry of data) {
|
||||||
|
switch (entry.type) {
|
||||||
|
case 'component': {
|
||||||
|
if (lastUpdate && entry.timestamp < lastUpdate) {
|
||||||
|
dropped.stale++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entry.id) {
|
||||||
|
dropped.noId++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set updates don't have a set field, the id is the set
|
||||||
|
const set = entry.msg.startsWith("set") ? entry.id : entry.set;
|
||||||
|
|
||||||
|
if (!set) {
|
||||||
|
dropped.noSet++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.ctype !== 'markers') {
|
||||||
|
dropped.unknownCType++;
|
||||||
|
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'),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (entry.msg.startsWith("set")) {
|
||||||
|
markerSetUpdates!.removed = update.removed;
|
||||||
|
markerSetUpdates!.payload = update.removed ? undefined : DynmapMapProvider.buildMarkerSet(set, entry);
|
||||||
|
} else if (entry.msg.startsWith("marker")) {
|
||||||
|
update.payload = update.removed ? undefined : DynmapMapProvider.buildMarker(entry);
|
||||||
|
markerSetUpdates!.markerUpdates.push(Object.freeze(update));
|
||||||
|
} else if (entry.msg.startsWith("area")) {
|
||||||
|
update.payload = update.removed ? undefined : DynmapMapProvider.buildArea(entry);
|
||||||
|
markerSetUpdates!.areaUpdates.push(Object.freeze(update));
|
||||||
|
|
||||||
|
} else if (entry.msg.startsWith("circle")) {
|
||||||
|
update.payload = update.removed ? undefined : DynmapMapProvider.buildCircle(entry);
|
||||||
|
markerSetUpdates!.circleUpdates.push(Object.freeze(update));
|
||||||
|
|
||||||
|
} else if (entry.msg.startsWith("line")) {
|
||||||
|
update.payload = update.removed ? undefined : DynmapMapProvider.buildLine(entry);
|
||||||
|
markerSetUpdates!.lineUpdates.push(Object.freeze(update));
|
||||||
|
}
|
||||||
|
|
||||||
|
accepted++;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'chat':
|
||||||
|
if (!entry.message || !entry.timestamp) {
|
||||||
|
dropped.incomplete++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.timestamp < lastUpdate) {
|
||||||
|
dropped.stale++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.source !== 'player' && entry.source !== 'web') {
|
||||||
|
dropped.notImplemented++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
updates.chat.push({
|
||||||
|
type: 'chat',
|
||||||
|
source: entry.source || undefined,
|
||||||
|
playerAccount: entry.account || undefined,
|
||||||
|
playerName: entry.playerName || undefined,
|
||||||
|
message: entry.message || "",
|
||||||
|
timestamp: entry.timestamp,
|
||||||
|
channel: entry.channel || undefined,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'playerjoin':
|
||||||
|
if (!entry.account || !entry.timestamp) {
|
||||||
|
dropped.incomplete++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.timestamp < lastUpdate) {
|
||||||
|
dropped.stale++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
updates.chat.push({
|
||||||
|
type: 'playerjoin',
|
||||||
|
playerAccount: entry.account,
|
||||||
|
playerName: entry.playerName || "",
|
||||||
|
timestamp: entry.timestamp || undefined,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'playerquit':
|
||||||
|
if (!entry.account || !entry.timestamp) {
|
||||||
|
dropped.incomplete++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.timestamp < lastUpdate) {
|
||||||
|
dropped.stale++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
updates.chat.push({
|
||||||
|
type: 'playerleave',
|
||||||
|
playerAccount: entry.account,
|
||||||
|
playerName: entry.playerName || "",
|
||||||
|
timestamp: entry.timestamp || undefined,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'tile':
|
||||||
|
if (!entry.name || !entry.timestamp) {
|
||||||
|
dropped.incomplete++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastUpdate && entry.timestamp < lastUpdate) {
|
||||||
|
dropped.stale++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
updates.tiles.push({
|
||||||
|
name: entry.name,
|
||||||
|
timestamp: entry.timestamp,
|
||||||
|
});
|
||||||
|
|
||||||
|
accepted++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dropped.unknownType++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sort chat by newest first
|
||||||
|
updates.chat = updates.chat.sort((one, two) => {
|
||||||
|
return two.timestamp - one.timestamp;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.debug(`Updates: ${accepted} accepted. Rejected: `, dropped);
|
||||||
|
|
||||||
|
return updates;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getMarkerSets(world: LiveAtlasWorldDefinition): Promise<Map<string, LiveAtlasMarkerSet>> {
|
||||||
|
const url = `${this.config.dynmap!.markers}_markers_/marker_${world.name}.json`;
|
||||||
|
|
||||||
|
if(this.markersAbort) {
|
||||||
|
this.markersAbort.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.markersAbort = new AbortController();
|
||||||
|
|
||||||
|
const response = await DynmapMapProvider.fetchJSON(url, this.markersAbort.signal);
|
||||||
|
const sets: Map<string, LiveAtlasMarkerSet> = new Map();
|
||||||
|
|
||||||
|
response.sets = response.sets || {};
|
||||||
|
|
||||||
|
for (const key in response.sets) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(response.sets, key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const set = response.sets[key],
|
||||||
|
markers = DynmapMapProvider.buildMarkers(set.markers || {}),
|
||||||
|
circles = DynmapMapProvider.buildCircles(set.circles || {}),
|
||||||
|
areas = DynmapMapProvider.buildAreas(set.areas || {}),
|
||||||
|
lines = DynmapMapProvider.buildLines(set.lines || {});
|
||||||
|
|
||||||
|
sets.set(key, {
|
||||||
|
...DynmapMapProvider.buildMarkerSet(key, set),
|
||||||
|
markers,
|
||||||
|
circles,
|
||||||
|
areas,
|
||||||
|
lines,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return sets;
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadServerConfiguration(): Promise<void> {
|
||||||
|
if(this.configurationAbort) {
|
||||||
|
this.configurationAbort.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.configurationAbort = new AbortController();
|
||||||
|
|
||||||
|
const response = await DynmapMapProvider.fetchJSON(this.config.dynmap!.configuration, this.configurationAbort.signal);
|
||||||
|
|
||||||
|
if (response.error === 'login-required') {
|
||||||
|
throw new Error("Login required");
|
||||||
|
} else if (response.error) {
|
||||||
|
throw new Error(response.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = DynmapMapProvider.buildServerConfig(response);
|
||||||
|
|
||||||
|
this.updateInterval = response.updaterate || 3000;
|
||||||
|
|
||||||
|
this.store.commit(MutationTypes.SET_SERVER_CONFIGURATION, config);
|
||||||
|
this.store.commit(MutationTypes.SET_SERVER_CONFIGURATION_HASH, response.confighash || 0);
|
||||||
|
this.store.commit(MutationTypes.SET_MAX_PLAYERS, response.maxcount || 0);
|
||||||
|
this.store.commit(MutationTypes.SET_SERVER_MESSAGES, DynmapMapProvider.buildMessagesConfig(response));
|
||||||
|
this.store.commit(MutationTypes.SET_WORLDS, this.buildWorlds(response));
|
||||||
|
this.store.commit(MutationTypes.SET_COMPONENTS, this.buildComponents(response));
|
||||||
|
this.store.commit(MutationTypes.SET_LOGGED_IN, response.loggedin || false);
|
||||||
|
}
|
||||||
|
|
||||||
|
async populateWorld(world: LiveAtlasWorldDefinition): Promise<void> {
|
||||||
|
const markerSets = await this.getMarkerSets(world);
|
||||||
|
|
||||||
|
useStore().commit(MutationTypes.SET_MARKER_SETS, markerSets);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getUpdate(): Promise<void> {
|
||||||
|
let url = this.config.dynmap!.update;
|
||||||
|
url = url.replace('{world}', this.store.state.currentWorld!.name);
|
||||||
|
url = url.replace('{timestamp}', this.updateTimestamp.getTime().toString());
|
||||||
|
|
||||||
|
if(this.updateAbort) {
|
||||||
|
this.updateAbort.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateAbort = new AbortController();
|
||||||
|
|
||||||
|
const response = await DynmapMapProvider.fetchJSON(url, this.updateAbort.signal);
|
||||||
|
const players: Set<LiveAtlasPlayer> = new Set(),
|
||||||
|
updates = this.buildUpdates(response.updates || []),
|
||||||
|
worldState = {
|
||||||
|
timeOfDay: response.servertime || 0,
|
||||||
|
thundering: response.isThundering || false,
|
||||||
|
raining: response.hasStorm || false,
|
||||||
|
};
|
||||||
|
|
||||||
|
(response.players || []).forEach((player: any) => {
|
||||||
|
const world = player.world && player.world !== '-some-other-bogus-world-' ? player.world : undefined;
|
||||||
|
|
||||||
|
players.add({
|
||||||
|
name: player.account || "",
|
||||||
|
displayName: player.name || "",
|
||||||
|
health: player.health || 0,
|
||||||
|
armor: player.armor || 0,
|
||||||
|
sort: player.sort || 0,
|
||||||
|
hidden: !world,
|
||||||
|
location: {
|
||||||
|
//Add 0.5 to position in the middle of a block
|
||||||
|
x: !isNaN(player.x) ? player.x + 0.5 : 0,
|
||||||
|
y: !isNaN(player.y) ? player.y : 0,
|
||||||
|
z: !isNaN(player.z) ? player.z + 0.5 : 0,
|
||||||
|
world: world,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//Extra fake players for testing
|
||||||
|
// for(let i = 0; i < 450; i++) {
|
||||||
|
// players.add({
|
||||||
|
// account: "VIDEO GAMES " + i,
|
||||||
|
// health: Math.round(Math.random() * 10),
|
||||||
|
// armor: Math.round(Math.random() * 10),
|
||||||
|
// name: "VIDEO GAMES " + i,
|
||||||
|
// sort: Math.round(Math.random() * 10),
|
||||||
|
// hidden: false,
|
||||||
|
// location: {
|
||||||
|
// x: Math.round(Math.random() * 1000) - 500,
|
||||||
|
// y: 64,
|
||||||
|
// z: Math.round(Math.random() * 1000) - 500,
|
||||||
|
// world: "world",
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
this.updateTimestamp = new Date(response.timestamp || 0);
|
||||||
|
|
||||||
|
this.store.commit(MutationTypes.SET_WORLD_STATE, worldState);
|
||||||
|
this.store.commit(MutationTypes.ADD_MARKER_SET_UPDATES, updates.markerSets);
|
||||||
|
this.store.commit(MutationTypes.ADD_TILE_UPDATES, updates.tiles);
|
||||||
|
this.store.commit(MutationTypes.ADD_CHAT, updates.chat);
|
||||||
|
this.store.commit(MutationTypes.SET_SERVER_CONFIGURATION_HASH, response.confighash || 0);
|
||||||
|
|
||||||
|
await this.store.dispatch(ActionTypes.SET_PLAYERS, players);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendChatMessage(message: string) {
|
||||||
|
const store = useStore();
|
||||||
|
|
||||||
|
if (!store.state.components.chatSending) {
|
||||||
|
return Promise.reject(store.state.messages.chatErrorDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(this.config.dynmap!.sendmessage, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: null,
|
||||||
|
message: message,
|
||||||
|
})
|
||||||
|
}).then((response) => {
|
||||||
|
if (response.status === 403) { //Rate limited
|
||||||
|
throw new ChatError(store.state.messages.chatErrorCooldown
|
||||||
|
.replace('%interval%', store.state.components.chatSending!.cooldown.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Network request failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
}).then(response => {
|
||||||
|
if (response.error !== 'none') {
|
||||||
|
throw new ChatError(store.state.messages.chatErrorNotAllowed);
|
||||||
|
}
|
||||||
|
}).catch(e => {
|
||||||
|
if (!(e instanceof ChatError)) {
|
||||||
|
console.error(store.state.messages.chatErrorUnknown);
|
||||||
|
console.trace(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
startUpdates() {
|
||||||
|
this.updatesEnabled = true;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async update() {
|
||||||
|
try {
|
||||||
|
await this.getUpdate();
|
||||||
|
} finally {
|
||||||
|
if(this.updatesEnabled) {
|
||||||
|
if(this.updateTimeout) {
|
||||||
|
clearTimeout(this.updateTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateTimeout = setTimeout(() => this.update(), this.updateInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stopUpdates() {
|
||||||
|
this.updatesEnabled = false;
|
||||||
|
|
||||||
|
if (this.updateTimeout) {
|
||||||
|
clearTimeout(this.updateTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.updateAbort) {
|
||||||
|
this.updateAbort.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.markersAbort) {
|
||||||
|
this.markersAbort.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
91
src/providers/MapProvider.ts
Normal file
91
src/providers/MapProvider.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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 {
|
||||||
|
HeadQueueEntry,
|
||||||
|
LiveAtlasMapProvider,
|
||||||
|
LiveAtlasServerDefinition,
|
||||||
|
LiveAtlasWorldDefinition
|
||||||
|
} from "@/index";
|
||||||
|
import {useStore} from "@/store";
|
||||||
|
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);
|
||||||
|
|
||||||
|
this.currentWorldUnwatch = watch(currentWorld, (newValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
this.populateWorld(newValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract loadServerConfiguration(): Promise<void>;
|
||||||
|
abstract populateWorld(world: LiveAtlasWorldDefinition): Promise<void>;
|
||||||
|
abstract sendChatMessage(message: string): void;
|
||||||
|
|
||||||
|
abstract startUpdates(): void;
|
||||||
|
abstract stopUpdates(): 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;
|
||||||
|
|
||||||
|
try {
|
||||||
|
response = await fetch(url, {signal});
|
||||||
|
} catch(e) {
|
||||||
|
if(e instanceof DOMException && e.name === 'AbortError') {
|
||||||
|
console.warn(`Request aborted (${url}`);
|
||||||
|
throw e;
|
||||||
|
} else {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Network request failed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Network request failed (${response.statusText || 'Unknown'})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
json = await response.json();
|
||||||
|
} catch(e) {
|
||||||
|
if(e instanceof DOMException && e.name === 'AbortError') {
|
||||||
|
console.warn(`Request aborted (${url}`);
|
||||||
|
throw e;
|
||||||
|
} else {
|
||||||
|
throw new Error('Request returned invalid json');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
520
src/providers/Pl3xmapMapProvider.ts
Normal file
520
src/providers/Pl3xmapMapProvider.ts
Normal file
@ -0,0 +1,520 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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 {
|
||||||
|
HeadQueueEntry, LiveAtlasArea, LiveAtlasCircle, LiveAtlasComponentConfig,
|
||||||
|
LiveAtlasDimension, LiveAtlasLine, LiveAtlasMarker,
|
||||||
|
LiveAtlasMarkerSet, LiveAtlasPartialComponentConfig,
|
||||||
|
LiveAtlasPlayer, LiveAtlasServerConfig, LiveAtlasServerDefinition,
|
||||||
|
LiveAtlasServerMessageConfig,
|
||||||
|
LiveAtlasWorldDefinition
|
||||||
|
} from "@/index";
|
||||||
|
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
||||||
|
import {MutationTypes} from "@/store/mutation-types";
|
||||||
|
import MapProvider from "@/providers/MapProvider";
|
||||||
|
import {ActionTypes} from "@/store/action-types";
|
||||||
|
import {titleColoursRegex} from "@/util";
|
||||||
|
|
||||||
|
export default class Pl3xmapMapProvider extends MapProvider {
|
||||||
|
private configurationAbort?: AbortController = undefined;
|
||||||
|
private markersAbort?: AbortController = undefined;
|
||||||
|
private playersAbort?: AbortController = undefined;
|
||||||
|
|
||||||
|
private updatesEnabled = false;
|
||||||
|
private updateTimeout: number = 0;
|
||||||
|
private updateTimestamp: Date = new Date();
|
||||||
|
private updateInterval: number = 3000;
|
||||||
|
private worldSettings: Map<string, {
|
||||||
|
components: LiveAtlasPartialComponentConfig,
|
||||||
|
}> = new Map();
|
||||||
|
|
||||||
|
constructor(config: LiveAtlasServerDefinition) {
|
||||||
|
super(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildServerConfig(response: any): LiveAtlasServerConfig {
|
||||||
|
return {
|
||||||
|
title: (response.ui?.title || 'Pl3xmap').replace(titleColoursRegex, ''),
|
||||||
|
expandUI: response.ui?.sidebar?.pinned === 'pinned',
|
||||||
|
|
||||||
|
//Not used by pl3xmap
|
||||||
|
defaultZoom: 1,
|
||||||
|
defaultMap: undefined,
|
||||||
|
defaultWorld: undefined,
|
||||||
|
followMap: undefined,
|
||||||
|
followZoom: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildMessagesConfig(response: any): LiveAtlasServerMessageConfig {
|
||||||
|
return {
|
||||||
|
worldsHeading: response.ui?.sidebar?.world_list_label || '',
|
||||||
|
playersHeading: response.ui?.sidebar?.player_list_label || '',
|
||||||
|
|
||||||
|
//Not used by pl3xmap
|
||||||
|
chatPlayerJoin: '',
|
||||||
|
chatPlayerQuit: '',
|
||||||
|
chatAnonymousJoin: '',
|
||||||
|
chatAnonymousQuit: '',
|
||||||
|
chatErrorNotAllowed: '',
|
||||||
|
chatErrorRequiresLogin: '',
|
||||||
|
chatErrorCooldown: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildWorlds(serverResponse: any, worldResponses: any[]): Array<LiveAtlasWorldDefinition> {
|
||||||
|
const worlds: Array<LiveAtlasWorldDefinition> = [];
|
||||||
|
|
||||||
|
(serverResponse.worlds || []).filter((w: any) => w && !!w.name).forEach((world: any, index: number) => {
|
||||||
|
const worldResponse = worldResponses[index],
|
||||||
|
worldConfig: {components: LiveAtlasPartialComponentConfig } = {
|
||||||
|
components: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
if(worldResponse.player_tracker?.enabled) {
|
||||||
|
worldConfig.components.playerMarkers = {
|
||||||
|
grayHiddenPlayers: true,
|
||||||
|
hideByDefault: !!worldResponse.player_tracker?.default_hidden,
|
||||||
|
layerName: worldResponse.player_tracker?.label || '',
|
||||||
|
layerPriority: worldResponse.player_tracker?.priority,
|
||||||
|
showBodies: false,
|
||||||
|
showSkinFaces: true,
|
||||||
|
showHealth: !!worldResponse.player_tracker?.nameplates?.show_health,
|
||||||
|
smallFaces: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.worldSettings.set(world.name, worldConfig);
|
||||||
|
|
||||||
|
if(!worldResponse) {
|
||||||
|
console.warn(`World ${world.name} has no matching world config. Ignoring.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dimension: LiveAtlasDimension = 'overworld';
|
||||||
|
|
||||||
|
if(world.type === 'nether') {
|
||||||
|
dimension = 'nether';
|
||||||
|
} else if(world.type === 'the_end') {
|
||||||
|
dimension = 'nether';
|
||||||
|
}
|
||||||
|
|
||||||
|
const maps: Map<string, LiveAtlasMapDefinition> = new Map();
|
||||||
|
|
||||||
|
maps.set('flat', Object.freeze(new LiveAtlasMapDefinition({
|
||||||
|
world: world,
|
||||||
|
|
||||||
|
background: 'transparent',
|
||||||
|
backgroundDay: 'transparent',
|
||||||
|
backgroundNight: 'transparent',
|
||||||
|
icon: undefined,
|
||||||
|
imageFormat: 'png',
|
||||||
|
name: 'flat',
|
||||||
|
displayName: 'Flat',
|
||||||
|
|
||||||
|
nativeZoomLevels: worldResponse.zoom.max || 1,
|
||||||
|
extraZoomLevels: worldResponse.zoom.extra || 0,
|
||||||
|
})));
|
||||||
|
|
||||||
|
worlds.push({
|
||||||
|
name: world.name || '(Unnamed world)',
|
||||||
|
displayName: world.display_name || world.name,
|
||||||
|
dimension,
|
||||||
|
protected: false,
|
||||||
|
seaLevel: 0,
|
||||||
|
height: 256,
|
||||||
|
center: {x: worldResponse.spawn.x, y: 0, z: worldResponse.spawn.z},
|
||||||
|
maps,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return Array.from(worlds.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildComponents(response: any): LiveAtlasComponentConfig {
|
||||||
|
const components: LiveAtlasComponentConfig = {
|
||||||
|
markers: {
|
||||||
|
showLabels: false,
|
||||||
|
},
|
||||||
|
coordinatesControl: undefined,
|
||||||
|
linkControl: !!response.ui?.link?.enabled,
|
||||||
|
layerControl: !!response.ui?.coordinates?.enabled,
|
||||||
|
|
||||||
|
//Configured per-world
|
||||||
|
playerMarkers: undefined,
|
||||||
|
|
||||||
|
//Not used by pl3xmap
|
||||||
|
chatBox: undefined,
|
||||||
|
chatBalloons: false,
|
||||||
|
clockControl: undefined,
|
||||||
|
logoControls: [],
|
||||||
|
login: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if(response.ui?.coordinates?.enabled) {
|
||||||
|
//Try to remove {x}/{z} placeholders are we aren't using them
|
||||||
|
const label = (response.ui?.coordinates?.html || "Location: ").replace(/{x}.*{z}/gi, '').trim(),
|
||||||
|
labelPlain = new DOMParser().parseFromString(label, 'text/html').body.textContent || "";
|
||||||
|
|
||||||
|
components.coordinatesControl = {
|
||||||
|
showY: false,
|
||||||
|
label: labelPlain,
|
||||||
|
showRegion: false,
|
||||||
|
showChunk: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getMarkerSets(world: LiveAtlasWorldDefinition): Promise<Map<string, LiveAtlasMarkerSet>> {
|
||||||
|
const url = `${this.config.pl3xmap}tiles/${world.name}/markers.json`;
|
||||||
|
|
||||||
|
if(this.markersAbort) {
|
||||||
|
this.markersAbort.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.markersAbort = new AbortController();
|
||||||
|
|
||||||
|
const response = await Pl3xmapMapProvider.fetchJSON(url, this.markersAbort.signal);
|
||||||
|
const sets: Map<string, LiveAtlasMarkerSet> = new Map();
|
||||||
|
|
||||||
|
if(!Array.isArray(response)) {
|
||||||
|
return sets;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.forEach(set => {
|
||||||
|
if(!set || !set.id) {
|
||||||
|
console.warn('Ignoring marker set without id');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = set.id;
|
||||||
|
|
||||||
|
const markers: Map<string, LiveAtlasMarker> = new Map(),
|
||||||
|
circles: Map<string, LiveAtlasCircle> = new Map(),
|
||||||
|
areas: Map<string, LiveAtlasArea> = new Map(),
|
||||||
|
lines: Map<string, LiveAtlasLine> = new Map();
|
||||||
|
|
||||||
|
(set.markers || []).forEach((marker: any) => {
|
||||||
|
switch(marker.type) {
|
||||||
|
case 'icon':
|
||||||
|
markers.set(`marker-${markers.size}`, Pl3xmapMapProvider.buildMarker(marker));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'polyline':
|
||||||
|
lines.set(`line-${lines.size}`, Pl3xmapMapProvider.buildLine(marker));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'rectangle':
|
||||||
|
areas.set(`area-${areas.size}`, Pl3xmapMapProvider.buildRectangle(marker));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'polygon':
|
||||||
|
areas.set(`area-${areas.size}`, Pl3xmapMapProvider.buildArea(marker));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'circle':
|
||||||
|
case 'ellipse':
|
||||||
|
circles.set(`circle-${circles.size}`, Pl3xmapMapProvider.buildCircle(marker));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.warn('Marker type ' + marker.type + ' not supported');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const e = {
|
||||||
|
id,
|
||||||
|
label: set.name || "Unnamed set",
|
||||||
|
hidden: set.hide || false,
|
||||||
|
priority: set.order || 0,
|
||||||
|
showLabels: false,
|
||||||
|
markers,
|
||||||
|
circles,
|
||||||
|
areas,
|
||||||
|
lines,
|
||||||
|
};
|
||||||
|
|
||||||
|
sets.set(id, e);
|
||||||
|
});
|
||||||
|
|
||||||
|
return sets;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildMarker(marker: any): LiveAtlasMarker {
|
||||||
|
return {
|
||||||
|
location: {
|
||||||
|
x: marker.point?.x || 0,
|
||||||
|
y: 0,
|
||||||
|
z: marker.point?.z || 0,
|
||||||
|
},
|
||||||
|
dimensions: marker.size ? [marker.size.x || 16, marker.size.z || 16] : [16, 16],
|
||||||
|
icon: marker.icon || "default",
|
||||||
|
|
||||||
|
label: (marker.tooltip || '').trim(),
|
||||||
|
isLabelHTML: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildRectangle(area: any): LiveAtlasArea {
|
||||||
|
return Object.seal({
|
||||||
|
style: {
|
||||||
|
stroke: typeof area.stroke !== 'undefined' ? !!area.stroke : true,
|
||||||
|
color: area.color || '#3388ff',
|
||||||
|
weight: area.weight || 3,
|
||||||
|
opacity: typeof area.opacity !== 'undefined' ? area.opacity : 1,
|
||||||
|
fill: typeof area.stroke !== 'undefined' ? !!area.stroke : true,
|
||||||
|
fillColor: area.fillColor || area.color || '#3388ff',
|
||||||
|
fillOpacity: area.fillOpacity || 0.2,
|
||||||
|
fillRule: area.fillRule,
|
||||||
|
},
|
||||||
|
points: [
|
||||||
|
area.points[0],
|
||||||
|
{x: area.points[0].x, z: area.points[1].z},
|
||||||
|
area.points[1],
|
||||||
|
{x: area.points[1].x, z: area.points[0].z},
|
||||||
|
],
|
||||||
|
outline: false,
|
||||||
|
|
||||||
|
tooltipContent: area.tooltip,
|
||||||
|
popupContent: area.popup,
|
||||||
|
isPopupHTML: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildArea(area: any): LiveAtlasArea {
|
||||||
|
return Object.seal({
|
||||||
|
style: {
|
||||||
|
stroke: typeof area.stroke !== 'undefined' ? !!area.stroke : true,
|
||||||
|
color: area.color || '#3388ff',
|
||||||
|
weight: area.weight || 3,
|
||||||
|
opacity: typeof area.opacity !== 'undefined' ? area.opacity : 1,
|
||||||
|
fill: typeof area.fill !== 'undefined' ? !!area.fill : true,
|
||||||
|
fillColor: area.fillColor || area.color || '#3388ff',
|
||||||
|
fillOpacity: area.fillOpacity || 0.2,
|
||||||
|
fillRule: area.fillRule,
|
||||||
|
},
|
||||||
|
points: area.points,
|
||||||
|
outline: false,
|
||||||
|
|
||||||
|
tooltipContent: area.tooltip,
|
||||||
|
popupContent: area.popup,
|
||||||
|
isPopupHTML: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildLine(line: any): LiveAtlasLine {
|
||||||
|
return Object.seal({
|
||||||
|
style: {
|
||||||
|
stroke: typeof line.stroke !== 'undefined' ? !!line.stroke : true,
|
||||||
|
color: line.color || '#3388ff',
|
||||||
|
weight: line.weight || 3,
|
||||||
|
opacity: typeof line.opacity !== 'undefined' ? line.opacity : 1,
|
||||||
|
},
|
||||||
|
points: line.points,
|
||||||
|
|
||||||
|
tooltipContent: line.tooltip,
|
||||||
|
popupContent: line.popup,
|
||||||
|
isPopupHTML: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildCircle(circle: any): LiveAtlasCircle {
|
||||||
|
return Object.seal({
|
||||||
|
location: {
|
||||||
|
x: circle.center?.x || 0,
|
||||||
|
y: 0,
|
||||||
|
z: circle.center?.z || 0,
|
||||||
|
},
|
||||||
|
radius: [circle.radiusX || circle.radius || 0, circle.radiusZ || circle.radius || 0],
|
||||||
|
style: {
|
||||||
|
stroke: typeof circle.stroke !== 'undefined' ? !!circle.stroke : true,
|
||||||
|
color: circle.color || '#3388ff',
|
||||||
|
weight: circle.weight || 3,
|
||||||
|
opacity: typeof circle.opacity !== 'undefined' ? circle.opacity : 1,
|
||||||
|
fill: typeof circle.stroke !== 'undefined' ? !!circle.stroke : true,
|
||||||
|
fillColor: circle.fillColor || circle.color || '#3388ff',
|
||||||
|
fillOpacity: circle.fillOpacity || 0.2,
|
||||||
|
fillRule: circle.fillRule,
|
||||||
|
},
|
||||||
|
|
||||||
|
tooltipContent: circle.tooltip,
|
||||||
|
popupContent: circle.popup,
|
||||||
|
isPopupHTML: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadServerConfiguration(): Promise<void> {
|
||||||
|
if(this.configurationAbort) {
|
||||||
|
this.configurationAbort.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.configurationAbort = new AbortController();
|
||||||
|
|
||||||
|
const baseUrl = this.config.pl3xmap,
|
||||||
|
response = await Pl3xmapMapProvider.fetchJSON(`${baseUrl}tiles/settings.json`, this.configurationAbort.signal);
|
||||||
|
|
||||||
|
if (response.error) {
|
||||||
|
throw new Error(response.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = Pl3xmapMapProvider.buildServerConfig(response),
|
||||||
|
worldNames: string[] = (response.worlds || []).filter((world: any) => world && !!world.name)
|
||||||
|
.map((world: any) => world.name);
|
||||||
|
|
||||||
|
const worldResponses = await Promise.all(worldNames.map(name =>
|
||||||
|
Pl3xmapMapProvider.fetchJSON(`${baseUrl}tiles/${name}/settings.json`, this.configurationAbort!.signal)));
|
||||||
|
|
||||||
|
this.store.commit(MutationTypes.SET_SERVER_CONFIGURATION, config);
|
||||||
|
this.store.commit(MutationTypes.SET_SERVER_MESSAGES, Pl3xmapMapProvider.buildMessagesConfig(response));
|
||||||
|
this.store.commit(MutationTypes.SET_WORLDS, this.buildWorlds(response, worldResponses));
|
||||||
|
this.store.commit(MutationTypes.SET_COMPONENTS, Pl3xmapMapProvider.buildComponents(response));
|
||||||
|
|
||||||
|
//Pl3xmap has no login functionality
|
||||||
|
this.store.commit(MutationTypes.SET_LOGGED_IN, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
async populateWorld(world: LiveAtlasWorldDefinition) {
|
||||||
|
const markerSets = await this.getMarkerSets(world),
|
||||||
|
worldConfig = this.worldSettings.get(world.name);
|
||||||
|
|
||||||
|
this.store.commit(MutationTypes.SET_MARKER_SETS, markerSets);
|
||||||
|
this.store.commit(MutationTypes.SET_COMPONENTS, worldConfig!.components);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getPlayers(): Promise<Set<LiveAtlasPlayer>> {
|
||||||
|
const url = `${this.config.pl3xmap}/tiles/players.json`;
|
||||||
|
|
||||||
|
if(this.playersAbort) {
|
||||||
|
this.playersAbort.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.playersAbort = new AbortController();
|
||||||
|
|
||||||
|
const response = await Pl3xmapMapProvider.fetchJSON(url, this.playersAbort.signal),
|
||||||
|
players: Set<LiveAtlasPlayer> = new Set();
|
||||||
|
|
||||||
|
(response.players || []).forEach((player: any) => {
|
||||||
|
console.log(player.uuid);
|
||||||
|
players.add({
|
||||||
|
name: (player.name || '').toLowerCase(),
|
||||||
|
uuid: player.uuid,
|
||||||
|
displayName: player.name || "",
|
||||||
|
health: player.health || 0,
|
||||||
|
armor: player.armor || 0,
|
||||||
|
sort: 0,
|
||||||
|
hidden: false,
|
||||||
|
location: {
|
||||||
|
//Add 0.5 to position in the middle of a block
|
||||||
|
x: !isNaN(player.x) ? player.x + 0.5 : 0,
|
||||||
|
y: 0,
|
||||||
|
z: !isNaN(player.z) ? player.z + 0.5 : 0,
|
||||||
|
world: player.world,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Extra fake players for testing
|
||||||
|
// for(let i = 0; i < 450; i++) {
|
||||||
|
// players.add({
|
||||||
|
// name: "VIDEO GAMES " + i,
|
||||||
|
// displayName: "VIDEO GAMES " + i,
|
||||||
|
// health: Math.round(Math.random() * 10),
|
||||||
|
// armor: Math.round(Math.random() * 10),
|
||||||
|
// sort: Math.round(Math.random() * 10),
|
||||||
|
// hidden: false,
|
||||||
|
// location: {
|
||||||
|
// x: Math.round(Math.random() * 1000) - 500,
|
||||||
|
// y: 0,
|
||||||
|
// z: Math.round(Math.random() * 1000) - 500,
|
||||||
|
// world: "world",
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
this.store.commit(MutationTypes.SET_MAX_PLAYERS, response.max || 0);
|
||||||
|
|
||||||
|
return players;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendChatMessage(message: string) {
|
||||||
|
throw new Error('Pl3xmap does not support chat');
|
||||||
|
}
|
||||||
|
|
||||||
|
startUpdates() {
|
||||||
|
this.updatesEnabled = true;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async update() {
|
||||||
|
try {
|
||||||
|
const players = await this.getPlayers();
|
||||||
|
|
||||||
|
this.updateTimestamp = new Date();
|
||||||
|
|
||||||
|
await this.store.dispatch(ActionTypes.SET_PLAYERS, players);
|
||||||
|
} finally {
|
||||||
|
if(this.updatesEnabled) {
|
||||||
|
if(this.updateTimeout) {
|
||||||
|
clearTimeout(this.updateTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateTimeout = setTimeout(() => this.update(), this.updateInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stopUpdates() {
|
||||||
|
this.updatesEnabled = false;
|
||||||
|
|
||||||
|
if (this.updateTimeout) {
|
||||||
|
clearTimeout(this.updateTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateTimeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTilesUrl(): string {
|
||||||
|
return `${this.config.pl3xmap}tiles/`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlayerHeadUrl(head: HeadQueueEntry): string {
|
||||||
|
//TODO: Listen to config
|
||||||
|
return 'https://mc-heads.net/avatar/{uuid}/16'.replace('{uuid}', head.uuid || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
getMarkerIconUrl(icon: string): string {
|
||||||
|
return `${this.config.pl3xmap}images/icon/registered/${icon}.png`;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
super.destroy();
|
||||||
|
|
||||||
|
if(this.configurationAbort) {
|
||||||
|
this.configurationAbort.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.playersAbort) {
|
||||||
|
this.playersAbort.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.markersAbort) {
|
||||||
|
this.markersAbort.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,22 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright 2021 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 "leaflet/controls";
|
@import "leaflet/controls";
|
||||||
@import "leaflet/popups";
|
@import "leaflet/popups";
|
||||||
|
@import "leaflet/tooltips";
|
||||||
@import "leaflet/markers";
|
@import "leaflet/markers";
|
||||||
|
|
||||||
.leaflet-container {
|
.leaflet-container {
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
/* Mixin for resetting :focus styles on browsers supporting :focus-visible */
|
/* Mixin for resetting :focus styles on browsers supporting :focus-visible */
|
||||||
@mixin focus-reset() {
|
@mixin focus-reset() {
|
||||||
&:focus:not(:focus-visible) {
|
&:focus:not(:focus-visible) {
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
.vue-notification-group {
|
.vue-notification-group {
|
||||||
z-index: 130 !important;
|
z-index: 130 !important;
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
/*!
|
/*!
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import "mixins";
|
@import "mixins";
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
/*!
|
/*!
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.leaflet-control {
|
.leaflet-control {
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
/*******************
|
/*******************
|
||||||
* players on the map
|
* players on the map
|
||||||
*/
|
*/
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
/*!
|
/*!
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.leaflet-popup {
|
.leaflet-popup {
|
||||||
|
28
src/scss/leaflet/_tooltips.scss
Normal file
28
src/scss/leaflet/_tooltips.scss
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.leaflet-tooltip {
|
||||||
|
background-color: var(--background-base);
|
||||||
|
color: var(--text-base);
|
||||||
|
box-shadow: var(--box-shadow);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
border: none;
|
||||||
|
will-change: transform;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,20 @@
|
|||||||
/*!
|
/*!
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
||||||
* These portions are Copyright 2020 Dynmap Contributors.
|
* These portions are Copyright 2020 Dynmap Contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
@import "mixins";
|
@import "mixins";
|
||||||
@import "placeholders";
|
@import "placeholders";
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export enum ActionTypes {
|
export enum ActionTypes {
|
||||||
LOAD_CONFIGURATION = "loadConfiguration",
|
LOAD_CONFIGURATION = "loadConfiguration",
|
||||||
GET_UPDATE = "getUpdate",
|
START_UPDATES = "startUpdates",
|
||||||
GET_MARKER_SETS = "getMarkerSets",
|
STOP_UPDATES = "stopUpdates",
|
||||||
SET_PLAYERS = "setPlayers",
|
SET_PLAYERS = "setPlayers",
|
||||||
POP_MARKER_UPDATES = "popMarkerUpdates",
|
POP_MARKER_UPDATES = "popMarkerUpdates",
|
||||||
POP_AREA_UPDATES = "popAreaUpdates",
|
POP_AREA_UPDATES = "popAreaUpdates",
|
||||||
@ -25,4 +25,4 @@ export enum ActionTypes {
|
|||||||
POP_LINE_UPDATES = "popLineUpdates",
|
POP_LINE_UPDATES = "popLineUpdates",
|
||||||
POP_TILE_UPDATES = "popTileUpdates",
|
POP_TILE_UPDATES = "popTileUpdates",
|
||||||
SEND_CHAT_MESSAGE = "sendChatMessage",
|
SEND_CHAT_MESSAGE = "sendChatMessage",
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {MutationTypes} from "@/store/mutation-types";
|
import {MutationTypes} from "@/store/mutation-types";
|
||||||
@ -20,15 +20,11 @@ import {State} from "@/store/state";
|
|||||||
import {ActionTypes} from "@/store/action-types";
|
import {ActionTypes} from "@/store/action-types";
|
||||||
import {Mutations} from "@/store/mutations";
|
import {Mutations} from "@/store/mutations";
|
||||||
import {
|
import {
|
||||||
DynmapAreaUpdate, DynmapCircleUpdate,
|
DynmapAreaUpdate, DynmapCircleUpdate, DynmapLineUpdate,
|
||||||
DynmapConfigurationResponse, DynmapLineUpdate,
|
|
||||||
DynmapMarkerSet,
|
|
||||||
DynmapMarkerUpdate,
|
DynmapMarkerUpdate,
|
||||||
DynmapPlayer, DynmapTileUpdate,
|
DynmapTileUpdate,
|
||||||
DynmapUpdateResponse
|
|
||||||
} from "@/dynmap";
|
} from "@/dynmap";
|
||||||
import {getAPI} from "@/util";
|
import {LiveAtlasMarkerSet, LiveAtlasPlayer, LiveAtlasWorldDefinition} from "@/index";
|
||||||
import {LiveAtlasWorldDefinition} from "@/index";
|
|
||||||
|
|
||||||
type AugmentedActionContext = {
|
type AugmentedActionContext = {
|
||||||
commit<K extends keyof Mutations>(
|
commit<K extends keyof Mutations>(
|
||||||
@ -40,17 +36,17 @@ type AugmentedActionContext = {
|
|||||||
export interface Actions {
|
export interface Actions {
|
||||||
[ActionTypes.LOAD_CONFIGURATION](
|
[ActionTypes.LOAD_CONFIGURATION](
|
||||||
{commit}: AugmentedActionContext,
|
{commit}: AugmentedActionContext,
|
||||||
):Promise<DynmapConfigurationResponse>
|
):Promise<void>
|
||||||
[ActionTypes.GET_UPDATE](
|
[ActionTypes.START_UPDATES](
|
||||||
{commit}: AugmentedActionContext,
|
{commit}: AugmentedActionContext,
|
||||||
):Promise<DynmapUpdateResponse>
|
):Promise<void>
|
||||||
[ActionTypes.GET_MARKER_SETS](
|
[ActionTypes.STOP_UPDATES](
|
||||||
{commit}: AugmentedActionContext,
|
{commit}: AugmentedActionContext,
|
||||||
):Promise<Map<string, DynmapMarkerSet>>
|
):Promise<void>
|
||||||
[ActionTypes.SET_PLAYERS](
|
[ActionTypes.SET_PLAYERS](
|
||||||
{commit}: AugmentedActionContext,
|
{commit}: AugmentedActionContext,
|
||||||
payload: Set<DynmapPlayer>
|
payload: Set<LiveAtlasPlayer>
|
||||||
):Promise<Map<string, DynmapMarkerSet>>
|
):Promise<Map<string, LiveAtlasMarkerSet>>
|
||||||
[ActionTypes.POP_MARKER_UPDATES](
|
[ActionTypes.POP_MARKER_UPDATES](
|
||||||
{commit}: AugmentedActionContext,
|
{commit}: AugmentedActionContext,
|
||||||
payload: {markerSet: string, amount: number}
|
payload: {markerSet: string, amount: number}
|
||||||
@ -78,21 +74,20 @@ export interface Actions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const actions: ActionTree<State, State> & Actions = {
|
export const actions: ActionTree<State, State> & Actions = {
|
||||||
async [ActionTypes.LOAD_CONFIGURATION]({commit, state}): Promise<DynmapConfigurationResponse> {
|
async [ActionTypes.LOAD_CONFIGURATION]({commit, state}): Promise<void> {
|
||||||
//Clear any existing has to avoid triggering a second config load, after this load changes the hash
|
//Clear any existing has to avoid triggering a second config load, after this load changes the hash
|
||||||
commit(MutationTypes.CLEAR_SERVER_CONFIGURATION_HASH, undefined);
|
commit(MutationTypes.CLEAR_SERVER_CONFIGURATION_HASH, undefined);
|
||||||
|
|
||||||
const config = await getAPI().getConfiguration();
|
if(!state.currentServer) {
|
||||||
|
console.warn('No current server');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
commit(MutationTypes.SET_SERVER_CONFIGURATION, config.config);
|
await state.currentMapProvider!.loadServerConfiguration();
|
||||||
commit(MutationTypes.SET_SERVER_MESSAGES, config.messages);
|
|
||||||
commit(MutationTypes.SET_WORLDS, config.worlds);
|
|
||||||
commit(MutationTypes.SET_COMPONENTS, config.components);
|
|
||||||
commit(MutationTypes.SET_LOGGED_IN, config.loggedIn);
|
|
||||||
|
|
||||||
//Skip default map/ui visibility logic if we already have a map selected (i.e config reload after hash change)
|
//Skip default map/ui visibility logic if we already have a map selected (i.e config reload after hash change)
|
||||||
if(state.currentMap) {
|
if(state.currentMap) {
|
||||||
return config;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Make UI visible if configured, there's enough space to do so, and this is the first config load
|
//Make UI visible if configured, there's enough space to do so, and this is the first config load
|
||||||
@ -104,8 +99,8 @@ export const actions: ActionTree<State, State> & Actions = {
|
|||||||
let worldName, mapName;
|
let worldName, mapName;
|
||||||
|
|
||||||
// Use config default world if it exists
|
// Use config default world if it exists
|
||||||
if(config.config.defaultWorld && state.worlds.has(config.config.defaultWorld)) {
|
if(state.configuration.defaultWorld && state.worlds.has(state.configuration.defaultWorld)) {
|
||||||
worldName = config.config.defaultWorld;
|
worldName = state.configuration.defaultWorld;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer world from parsed url if present and it exists
|
// Prefer world from parsed url if present and it exists
|
||||||
@ -122,8 +117,8 @@ export const actions: ActionTree<State, State> & Actions = {
|
|||||||
const world = state.worlds.get(worldName) as LiveAtlasWorldDefinition;
|
const world = state.worlds.get(worldName) as LiveAtlasWorldDefinition;
|
||||||
|
|
||||||
// Use config default map if it exists
|
// Use config default map if it exists
|
||||||
if(config.config.defaultMap && world.maps.has(config.config.defaultMap)) {
|
if(state.configuration.defaultMap && world.maps.has(state.configuration.defaultMap)) {
|
||||||
mapName = config.config.defaultMap;
|
mapName = state.configuration.defaultMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer map from parsed url if present and it exists
|
// Prefer map from parsed url if present and it exists
|
||||||
@ -142,40 +137,35 @@ export const actions: ActionTree<State, State> & Actions = {
|
|||||||
worldName, mapName
|
worldName, mapName
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async [ActionTypes.GET_UPDATE]({commit, dispatch, state}) {
|
async [ActionTypes.START_UPDATES]({state}) {
|
||||||
if(!state.currentWorld) {
|
if(!state.currentWorld) {
|
||||||
return Promise.reject("No current world");
|
return Promise.reject("No current world");
|
||||||
}
|
}
|
||||||
|
|
||||||
const update = await getAPI().getUpdate(state.updateRequestId, state.currentWorld.name, state.updateTimestamp.valueOf());
|
state.currentMapProvider!.startUpdates();
|
||||||
|
|
||||||
commit(MutationTypes.SET_WORLD_STATE, update.worldState);
|
|
||||||
commit(MutationTypes.SET_UPDATE_TIMESTAMP, new Date(update.timestamp));
|
|
||||||
commit(MutationTypes.INCREMENT_REQUEST_ID, undefined);
|
|
||||||
commit(MutationTypes.ADD_MARKER_SET_UPDATES, update.updates.markerSets);
|
|
||||||
commit(MutationTypes.ADD_TILE_UPDATES, update.updates.tiles);
|
|
||||||
commit(MutationTypes.ADD_CHAT, update.updates.chat);
|
|
||||||
commit(MutationTypes.SET_SERVER_CONFIGURATION_HASH, update.configHash);
|
|
||||||
|
|
||||||
await dispatch(ActionTypes.SET_PLAYERS, update.players);
|
|
||||||
return update;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
[ActionTypes.SET_PLAYERS]({commit, state}, players: Set<DynmapPlayer>) {
|
async [ActionTypes.STOP_UPDATES]({state}) {
|
||||||
|
if(!state.currentWorld) {
|
||||||
|
return Promise.reject("No current world");
|
||||||
|
}
|
||||||
|
|
||||||
|
state.currentMapProvider!.stopUpdates();
|
||||||
|
},
|
||||||
|
|
||||||
|
[ActionTypes.SET_PLAYERS]({commit, state}, players: Set<LiveAtlasPlayer>) {
|
||||||
const keep: Set<string> = new Set();
|
const keep: Set<string> = new Set();
|
||||||
|
|
||||||
for(const player of players) {
|
for(const player of players) {
|
||||||
keep.add(player.account);
|
keep.add(player.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Remove any players that aren't in the set
|
//Remove any players that aren't in the set
|
||||||
commit(MutationTypes.SYNC_PLAYERS, keep);
|
commit(MutationTypes.SYNC_PLAYERS, keep);
|
||||||
|
|
||||||
const processQueue = (players: Set<DynmapPlayer>, resolve: Function) => {
|
const processQueue = (players: Set<LiveAtlasPlayer>, resolve: Function) => {
|
||||||
commit(MutationTypes.SET_PLAYERS_ASYNC, players);
|
commit(MutationTypes.SET_PLAYERS_ASYNC, players);
|
||||||
|
|
||||||
if(!players.size) {
|
if(!players.size) {
|
||||||
@ -191,17 +181,6 @@ export const actions: ActionTree<State, State> & Actions = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async [ActionTypes.GET_MARKER_SETS]({commit, state}) {
|
|
||||||
if(!state.currentWorld) {
|
|
||||||
throw new Error("No current world");
|
|
||||||
}
|
|
||||||
|
|
||||||
const markerSets = await getAPI().getMarkerSets(state.currentWorld.name)
|
|
||||||
commit(MutationTypes.SET_MARKER_SETS, markerSets);
|
|
||||||
|
|
||||||
return markerSets;
|
|
||||||
},
|
|
||||||
|
|
||||||
async [ActionTypes.POP_MARKER_UPDATES]({commit, state}, {markerSet, amount}: {markerSet: string, amount: number}): Promise<DynmapMarkerUpdate[]> {
|
async [ActionTypes.POP_MARKER_UPDATES]({commit, state}, {markerSet, amount}: {markerSet: string, amount: number}): Promise<DynmapMarkerUpdate[]> {
|
||||||
if(!state.markerSets.has(markerSet)) {
|
if(!state.markerSets.has(markerSet)) {
|
||||||
console.warn(`POP_MARKER_UPDATES: Marker set ${markerSet} doesn't exist`);
|
console.warn(`POP_MARKER_UPDATES: Marker set ${markerSet} doesn't exist`);
|
||||||
@ -263,6 +242,6 @@ export const actions: ActionTree<State, State> & Actions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async [ActionTypes.SEND_CHAT_MESSAGE]({commit, state}, message: string): Promise<void> {
|
async [ActionTypes.SEND_CHAT_MESSAGE]({commit, state}, message: string): Promise<void> {
|
||||||
await getAPI().sendChatMessage(message);
|
await state.currentMapProvider!.sendChatMessage(message);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,22 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {GetterTree} from "vuex";
|
import {GetterTree} from "vuex";
|
||||||
import {State} from "@/store/state";
|
import {State} from "@/store/state";
|
||||||
import {getMinecraftTime, getUrlForLocation} from "@/util";
|
import {getMinecraftTime, getUrlForLocation} from "@/util";
|
||||||
import {LiveAtlasDynmapServerDefinition} from "@/index";
|
|
||||||
|
|
||||||
export type Getters = {
|
export type Getters = {
|
||||||
playerMarkersEnabled(state: State): boolean;
|
playerMarkersEnabled(state: State): boolean;
|
||||||
@ -26,7 +25,6 @@ export type Getters = {
|
|||||||
night(state: State): boolean;
|
night(state: State): boolean;
|
||||||
mapBackground(state: State, getters: GetterTree<State, State> & Getters): string;
|
mapBackground(state: State, getters: GetterTree<State, State> & Getters): string;
|
||||||
url(state: State, getters: GetterTree<State, State> & Getters): string;
|
url(state: State, getters: GetterTree<State, State> & Getters): string;
|
||||||
serverConfig(state: State, getters: GetterTree<State, State> & Getters): LiveAtlasDynmapServerDefinition;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getters: GetterTree<State, State> & Getters = {
|
export const getters: GetterTree<State, State> & Getters = {
|
||||||
@ -74,12 +72,4 @@ export const getters: GetterTree<State, State> & Getters = {
|
|||||||
|
|
||||||
return getUrlForLocation(state.currentMap, {x,y,z}, zoom);
|
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;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export enum MutationTypes {
|
export enum MutationTypes {
|
||||||
@ -28,7 +28,6 @@ export enum MutationTypes {
|
|||||||
CLEAR_MARKER_SETS = 'clearMarkerSets',
|
CLEAR_MARKER_SETS = 'clearMarkerSets',
|
||||||
ADD_WORLD = 'addWorld',
|
ADD_WORLD = 'addWorld',
|
||||||
SET_WORLD_STATE = 'setWorldState',
|
SET_WORLD_STATE = 'setWorldState',
|
||||||
SET_UPDATE_TIMESTAMP = 'setUpdateTimestamp',
|
|
||||||
ADD_MARKER_SET_UPDATES = 'addMarkerSetUpdates',
|
ADD_MARKER_SET_UPDATES = 'addMarkerSetUpdates',
|
||||||
ADD_TILE_UPDATES = 'addTileUpdates',
|
ADD_TILE_UPDATES = 'addTileUpdates',
|
||||||
ADD_CHAT = 'addChat',
|
ADD_CHAT = 'addChat',
|
||||||
@ -37,7 +36,7 @@ export enum MutationTypes {
|
|||||||
POP_CIRCLE_UPDATES = 'popCircleUpdates',
|
POP_CIRCLE_UPDATES = 'popCircleUpdates',
|
||||||
POP_LINE_UPDATES = 'popLineUpdates',
|
POP_LINE_UPDATES = 'popLineUpdates',
|
||||||
POP_TILE_UPDATES = 'popTileUpdates',
|
POP_TILE_UPDATES = 'popTileUpdates',
|
||||||
INCREMENT_REQUEST_ID = 'incrementRequestId',
|
SET_MAX_PLAYERS = 'setMaxPlayers',
|
||||||
SET_PLAYERS_ASYNC = 'setPlayersAsync',
|
SET_PLAYERS_ASYNC = 'setPlayersAsync',
|
||||||
CLEAR_PLAYERS = 'clearPlayers',
|
CLEAR_PLAYERS = 'clearPlayers',
|
||||||
SYNC_PLAYERS = 'syncPlayers',
|
SYNC_PLAYERS = 'syncPlayers',
|
||||||
|
@ -1,32 +1,25 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {MutationTree} from "vuex";
|
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 {
|
||||||
DynmapArea,
|
|
||||||
DynmapCircle,
|
|
||||||
DynmapComponentConfig,
|
|
||||||
DynmapLine, DynmapMarker,
|
|
||||||
DynmapMarkerSet,
|
|
||||||
DynmapMarkerSetUpdates,
|
DynmapMarkerSetUpdates,
|
||||||
DynmapPlayer,
|
DynmapTileUpdate
|
||||||
DynmapServerConfig, DynmapTileUpdate,
|
|
||||||
DynmapChat
|
|
||||||
} from "@/dynmap";
|
} from "@/dynmap";
|
||||||
import {
|
import {
|
||||||
Coordinate,
|
Coordinate,
|
||||||
@ -38,8 +31,18 @@ import {
|
|||||||
LiveAtlasParsedUrl,
|
LiveAtlasParsedUrl,
|
||||||
LiveAtlasGlobalConfig,
|
LiveAtlasGlobalConfig,
|
||||||
LiveAtlasGlobalMessageConfig,
|
LiveAtlasGlobalMessageConfig,
|
||||||
LiveAtlasServerMessageConfig
|
LiveAtlasServerMessageConfig,
|
||||||
|
LiveAtlasPlayer,
|
||||||
|
LiveAtlasCircle,
|
||||||
|
LiveAtlasLine,
|
||||||
|
LiveAtlasArea,
|
||||||
|
LiveAtlasMarker,
|
||||||
|
LiveAtlasMarkerSet,
|
||||||
|
LiveAtlasServerDefinition,
|
||||||
|
LiveAtlasServerConfig, LiveAtlasChat, LiveAtlasPartialComponentConfig, LiveAtlasComponentConfig
|
||||||
} from "@/index";
|
} from "@/index";
|
||||||
|
import DynmapMapProvider from "@/providers/DynmapMapProvider";
|
||||||
|
import Pl3xmapMapProvider from "@/providers/Pl3xmapMapProvider";
|
||||||
|
|
||||||
export type CurrentMapPayload = {
|
export type CurrentMapPayload = {
|
||||||
worldName: string;
|
worldName: string;
|
||||||
@ -48,21 +51,20 @@ export type CurrentMapPayload = {
|
|||||||
|
|
||||||
export type Mutations<S = State> = {
|
export type Mutations<S = State> = {
|
||||||
[MutationTypes.INIT](state: S, config: LiveAtlasGlobalConfig): void
|
[MutationTypes.INIT](state: S, config: LiveAtlasGlobalConfig): void
|
||||||
[MutationTypes.SET_SERVER_CONFIGURATION](state: S, config: DynmapServerConfig): void
|
[MutationTypes.SET_SERVER_CONFIGURATION](state: S, config: LiveAtlasServerConfig): void
|
||||||
[MutationTypes.SET_SERVER_CONFIGURATION_HASH](state: S, hash: number): void
|
[MutationTypes.SET_SERVER_CONFIGURATION_HASH](state: S, hash: number): void
|
||||||
[MutationTypes.CLEAR_SERVER_CONFIGURATION_HASH](state: S): void
|
[MutationTypes.CLEAR_SERVER_CONFIGURATION_HASH](state: S): void
|
||||||
[MutationTypes.SET_SERVER_MESSAGES](state: S, messages: LiveAtlasServerMessageConfig): void
|
[MutationTypes.SET_SERVER_MESSAGES](state: S, messages: LiveAtlasServerMessageConfig): void
|
||||||
[MutationTypes.SET_WORLDS](state: S, worlds: Array<LiveAtlasWorldDefinition>): void
|
[MutationTypes.SET_WORLDS](state: S, worlds: Array<LiveAtlasWorldDefinition>): void
|
||||||
[MutationTypes.CLEAR_WORLDS](state: S): void
|
[MutationTypes.CLEAR_WORLDS](state: S): void
|
||||||
[MutationTypes.SET_COMPONENTS](state: S, worlds: DynmapComponentConfig): void
|
[MutationTypes.SET_COMPONENTS](state: S, components: LiveAtlasPartialComponentConfig | LiveAtlasComponentConfig): void
|
||||||
[MutationTypes.SET_MARKER_SETS](state: S, worlds: Map<string, DynmapMarkerSet>): void
|
[MutationTypes.SET_MARKER_SETS](state: S, worlds: Map<string, LiveAtlasMarkerSet>): void
|
||||||
[MutationTypes.CLEAR_MARKER_SETS](state: S): void
|
[MutationTypes.CLEAR_MARKER_SETS](state: S): void
|
||||||
[MutationTypes.ADD_WORLD](state: S, world: LiveAtlasWorldDefinition): void
|
[MutationTypes.ADD_WORLD](state: S, world: LiveAtlasWorldDefinition): void
|
||||||
[MutationTypes.SET_WORLD_STATE](state: S, worldState: LiveAtlasWorldState): void
|
[MutationTypes.SET_WORLD_STATE](state: S, worldState: LiveAtlasWorldState): void
|
||||||
[MutationTypes.SET_UPDATE_TIMESTAMP](state: S, time: Date): void
|
|
||||||
[MutationTypes.ADD_MARKER_SET_UPDATES](state: S, updates: Map<string, DynmapMarkerSetUpdates>): void
|
[MutationTypes.ADD_MARKER_SET_UPDATES](state: S, updates: Map<string, DynmapMarkerSetUpdates>): void
|
||||||
[MutationTypes.ADD_TILE_UPDATES](state: S, updates: Array<DynmapTileUpdate>): void
|
[MutationTypes.ADD_TILE_UPDATES](state: S, updates: Array<DynmapTileUpdate>): void
|
||||||
[MutationTypes.ADD_CHAT](state: State, chat: Array<DynmapChat>): void
|
[MutationTypes.ADD_CHAT](state: State, chat: Array<LiveAtlasChat>): void
|
||||||
|
|
||||||
[MutationTypes.POP_MARKER_UPDATES](state: S, payload: {markerSet: string, amount: number}): 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_AREA_UPDATES](state: S, payload: {markerSet: string, amount: number}): void
|
||||||
@ -70,8 +72,8 @@ export type Mutations<S = State> = {
|
|||||||
[MutationTypes.POP_LINE_UPDATES](state: S, payload: {markerSet: string, amount: number}): void
|
[MutationTypes.POP_LINE_UPDATES](state: S, payload: {markerSet: string, amount: number}): void
|
||||||
[MutationTypes.POP_TILE_UPDATES](state: S, amount: number): void
|
[MutationTypes.POP_TILE_UPDATES](state: S, amount: number): void
|
||||||
|
|
||||||
[MutationTypes.INCREMENT_REQUEST_ID](state: S): void
|
[MutationTypes.SET_MAX_PLAYERS](state: S, maxPlayers: number): void
|
||||||
[MutationTypes.SET_PLAYERS_ASYNC](state: S, players: Set<DynmapPlayer>): Set<DynmapPlayer>
|
[MutationTypes.SET_PLAYERS_ASYNC](state: S, players: Set<LiveAtlasPlayer>): Set<LiveAtlasPlayer>
|
||||||
[MutationTypes.SYNC_PLAYERS](state: S, keep: Set<string>): void
|
[MutationTypes.SYNC_PLAYERS](state: S, keep: Set<string>): void
|
||||||
[MutationTypes.CLEAR_PLAYERS](state: S): void
|
[MutationTypes.CLEAR_PLAYERS](state: S): void
|
||||||
[MutationTypes.SET_CURRENT_SERVER](state: S, server: string): void
|
[MutationTypes.SET_CURRENT_SERVER](state: S, server: string): void
|
||||||
@ -81,8 +83,8 @@ export type Mutations<S = State> = {
|
|||||||
[MutationTypes.SET_PARSED_URL](state: S, payload: LiveAtlasParsedUrl): void
|
[MutationTypes.SET_PARSED_URL](state: S, payload: LiveAtlasParsedUrl): void
|
||||||
[MutationTypes.CLEAR_PARSED_URL](state: S): void
|
[MutationTypes.CLEAR_PARSED_URL](state: S): void
|
||||||
[MutationTypes.CLEAR_CURRENT_MAP](state: S): void
|
[MutationTypes.CLEAR_CURRENT_MAP](state: S): void
|
||||||
[MutationTypes.SET_FOLLOW_TARGET](state: S, payload: DynmapPlayer): void
|
[MutationTypes.SET_FOLLOW_TARGET](state: S, payload: LiveAtlasPlayer): void
|
||||||
[MutationTypes.SET_PAN_TARGET](state: S, payload: DynmapPlayer): void
|
[MutationTypes.SET_PAN_TARGET](state: S, payload: LiveAtlasPlayer): void
|
||||||
[MutationTypes.CLEAR_FOLLOW_TARGET](state: S, a?: void): void
|
[MutationTypes.CLEAR_FOLLOW_TARGET](state: S, a?: void): void
|
||||||
[MutationTypes.CLEAR_PAN_TARGET](state: S, a?: void): void
|
[MutationTypes.CLEAR_PAN_TARGET](state: S, a?: void): void
|
||||||
|
|
||||||
@ -158,9 +160,8 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Sets configuration options from the initial config fetch
|
// Sets configuration options from the initial config fetch
|
||||||
[MutationTypes.SET_SERVER_CONFIGURATION](state: State, config: DynmapServerConfig) {
|
[MutationTypes.SET_SERVER_CONFIGURATION](state: State, config: LiveAtlasServerConfig) {
|
||||||
state.configuration = Object.assign(state.configuration, config);
|
state.configuration = Object.assign(state.configuration, config);
|
||||||
state.configurationHash = config.hash;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Sets configuration hash
|
// Sets configuration hash
|
||||||
@ -222,13 +223,15 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
state.currentWorldState.thundering = false;
|
state.currentWorldState.thundering = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
//Sets the state and settings of optional components, from the initial config fetch
|
//Updates the state of optional components (chat, link button, etc)
|
||||||
[MutationTypes.SET_COMPONENTS](state: State, components: DynmapComponentConfig) {
|
//Can be called with a LiveAtlasComponentConfig object to replace the whole state
|
||||||
|
//or a LiveAtlasPartialComponentConfig object for partial updates to the existing state
|
||||||
|
[MutationTypes.SET_COMPONENTS](state: State, components: LiveAtlasPartialComponentConfig | LiveAtlasComponentConfig) {
|
||||||
state.components = Object.assign(state.components, components);
|
state.components = Object.assign(state.components, components);
|
||||||
},
|
},
|
||||||
|
|
||||||
//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, LiveAtlasMarkerSet>) {
|
||||||
state.markerSets.clear();
|
state.markerSets.clear();
|
||||||
state.pendingSetUpdates.clear();
|
state.pendingSetUpdates.clear();
|
||||||
|
|
||||||
@ -257,11 +260,6 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
state.currentWorldState = Object.assign(state.currentWorldState, worldState);
|
state.currentWorldState = Object.assign(state.currentWorldState, worldState);
|
||||||
},
|
},
|
||||||
|
|
||||||
//Sets the timestamp of the last update fetch
|
|
||||||
[MutationTypes.SET_UPDATE_TIMESTAMP](state: State, timestamp: Date) {
|
|
||||||
state.updateTimestamp = timestamp;
|
|
||||||
},
|
|
||||||
|
|
||||||
//Adds markerset related updates from an update fetch to the pending updates list
|
//Adds markerset related updates from an update fetch to the pending updates list
|
||||||
[MutationTypes.ADD_MARKER_SET_UPDATES](state: State, updates: Map<string, DynmapMarkerSetUpdates>) {
|
[MutationTypes.ADD_MARKER_SET_UPDATES](state: State, updates: Map<string, DynmapMarkerSetUpdates>) {
|
||||||
for(const entry of updates) {
|
for(const entry of updates) {
|
||||||
@ -277,10 +275,10 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
priority: entry[1].payload.priority,
|
priority: entry[1].payload.priority,
|
||||||
label: entry[1].payload.label,
|
label: entry[1].payload.label,
|
||||||
hidden: entry[1].payload.hidden,
|
hidden: entry[1].payload.hidden,
|
||||||
markers: Object.freeze(new Map()) as Map<string, DynmapMarker>,
|
markers: Object.freeze(new Map()) as Map<string, LiveAtlasMarker>,
|
||||||
areas: Object.freeze(new Map()) as Map<string, DynmapArea>,
|
areas: Object.freeze(new Map()) as Map<string, LiveAtlasArea>,
|
||||||
circles: Object.freeze(new Map()) as Map<string, DynmapCircle>,
|
circles: Object.freeze(new Map()) as Map<string, LiveAtlasCircle>,
|
||||||
lines: Object.freeze(new Map()) as Map<string, DynmapLine>,
|
lines: Object.freeze(new Map()) as Map<string, LiveAtlasLine>,
|
||||||
});
|
});
|
||||||
|
|
||||||
state.pendingSetUpdates.set(entry[0], {
|
state.pendingSetUpdates.set(entry[0], {
|
||||||
@ -295,7 +293,7 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const set = state.markerSets.get(entry[0]) as DynmapMarkerSet,
|
const set = state.markerSets.get(entry[0]) as LiveAtlasMarkerSet,
|
||||||
setUpdates = state.pendingSetUpdates.get(entry[0]) as DynmapMarkerSetUpdates;
|
setUpdates = state.pendingSetUpdates.get(entry[0]) as DynmapMarkerSetUpdates;
|
||||||
|
|
||||||
//Delete the set if it has been deleted
|
//Delete the set if it has been deleted
|
||||||
@ -320,7 +318,7 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
if(update.removed) {
|
if(update.removed) {
|
||||||
set.markers.delete(update.id);
|
set.markers.delete(update.id);
|
||||||
} else {
|
} else {
|
||||||
set.markers.set(update.id, update.payload as DynmapMarker);
|
set.markers.set(update.id, update.payload as LiveAtlasMarker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,7 +326,7 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
if(update.removed) {
|
if(update.removed) {
|
||||||
set.areas.delete(update.id);
|
set.areas.delete(update.id);
|
||||||
} else {
|
} else {
|
||||||
set.areas.set(update.id, update.payload as DynmapArea);
|
set.areas.set(update.id, update.payload as LiveAtlasArea);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,7 +334,7 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
if(update.removed) {
|
if(update.removed) {
|
||||||
set.circles.delete(update.id);
|
set.circles.delete(update.id);
|
||||||
} else {
|
} else {
|
||||||
set.circles.set(update.id, update.payload as DynmapCircle);
|
set.circles.set(update.id, update.payload as LiveAtlasCircle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,7 +342,7 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
if(update.removed) {
|
if(update.removed) {
|
||||||
set.lines.delete(update.id);
|
set.lines.delete(update.id);
|
||||||
} else {
|
} else {
|
||||||
set.lines.set(update.id, update.payload as DynmapLine);
|
set.lines.set(update.id, update.payload as LiveAtlasLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,7 +360,7 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
//Adds chat messages from an update fetch to the chat history
|
//Adds chat messages from an update fetch to the chat history
|
||||||
[MutationTypes.ADD_CHAT](state: State, chat: Array<DynmapChat>) {
|
[MutationTypes.ADD_CHAT](state: State, chat: Array<LiveAtlasChat>) {
|
||||||
state.chat.messages.unshift(...chat);
|
state.chat.messages.unshift(...chat);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -411,37 +409,38 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
state.pendingTileUpdates.splice(0, amount);
|
state.pendingTileUpdates.splice(0, amount);
|
||||||
},
|
},
|
||||||
|
|
||||||
//Increments the request id for the next update fetch
|
[MutationTypes.SET_MAX_PLAYERS](state: State, maxPlayers: number) {
|
||||||
[MutationTypes.INCREMENT_REQUEST_ID](state: State) {
|
state.maxPlayers = maxPlayers;
|
||||||
state.updateRequestId++;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Set up to 10 players at once
|
// Set up to 10 players at once
|
||||||
[MutationTypes.SET_PLAYERS_ASYNC](state: State, players: Set<DynmapPlayer>): Set<DynmapPlayer> {
|
[MutationTypes.SET_PLAYERS_ASYNC](state: State, players: Set<LiveAtlasPlayer>): Set<LiveAtlasPlayer> {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
for(const player of players) {
|
for(const player of players) {
|
||||||
if(state.players.has(player.account)) {
|
if(state.players.has(player.name)) {
|
||||||
const existing = state.players.get(player.account);
|
const existing = state.players.get(player.name);
|
||||||
|
|
||||||
existing!.health = player.health;
|
existing!.health = player.health;
|
||||||
|
existing!.uuid = player.uuid;
|
||||||
existing!.armor = player.armor;
|
existing!.armor = player.armor;
|
||||||
existing!.location = Object.assign(existing!.location, player.location);
|
existing!.location = Object.assign(existing!.location, player.location);
|
||||||
existing!.hidden = player.hidden;
|
existing!.hidden = player.hidden;
|
||||||
existing!.name = player.name;
|
existing!.displayName = player.displayName;
|
||||||
existing!.sort = player.sort;
|
existing!.sort = player.sort;
|
||||||
|
|
||||||
if(existing!.name !== player.name || existing!.sort !== player.sort) {
|
if(existing!.displayName !== player.displayName || existing!.sort !== player.sort) {
|
||||||
state.sortedPlayers.dirty = true;
|
state.sortedPlayers.dirty = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state.sortedPlayers.dirty = true;
|
state.sortedPlayers.dirty = true;
|
||||||
state.players.set(player.account, {
|
state.players.set(player.name, {
|
||||||
account: player.account,
|
name: player.name,
|
||||||
|
uuid: player.uuid,
|
||||||
health: player.health,
|
health: player.health,
|
||||||
armor: player.armor,
|
armor: player.armor,
|
||||||
location: player.location,
|
location: player.location,
|
||||||
name: player.name,
|
displayName: player.displayName,
|
||||||
sort: player.sort,
|
sort: player.sort,
|
||||||
hidden: player.hidden,
|
hidden: player.hidden,
|
||||||
});
|
});
|
||||||
@ -461,7 +460,7 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
return a.sort - b.sort;
|
return a.sort - b.sort;
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.account.toLowerCase().localeCompare(b.account.toLowerCase());
|
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
||||||
}) as LiveAtlasSortedPlayers;
|
}) as LiveAtlasSortedPlayers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,7 +470,7 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
//Removes all players not found in the provided keep set
|
//Removes all players not found in the provided keep set
|
||||||
[MutationTypes.SYNC_PLAYERS](state: State, keep: Set<string>) {
|
[MutationTypes.SYNC_PLAYERS](state: State, keep: Set<string>) {
|
||||||
for(const [key, player] of state.players) {
|
for(const [key, player] of state.players) {
|
||||||
if(!keep.has(player.account)) {
|
if(!keep.has(player.name)) {
|
||||||
state.sortedPlayers.splice(state.sortedPlayers.indexOf(player), 1);
|
state.sortedPlayers.splice(state.sortedPlayers.indexOf(player), 1);
|
||||||
state.players.delete(key);
|
state.players.delete(key);
|
||||||
}
|
}
|
||||||
@ -493,6 +492,22 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.currentServer = state.servers.get(serverName);
|
state.currentServer = state.servers.get(serverName);
|
||||||
|
|
||||||
|
if(state.currentMapProvider) {
|
||||||
|
state.currentMapProvider.stopUpdates();
|
||||||
|
state.currentMapProvider.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(state.currentServer!.type) {
|
||||||
|
case 'pl3xmap':
|
||||||
|
state.currentMapProvider = Object.seal(
|
||||||
|
new Pl3xmapMapProvider(state.servers.get(serverName) as LiveAtlasServerDefinition));
|
||||||
|
break;
|
||||||
|
case 'dynmap':
|
||||||
|
state.currentMapProvider = Object.seal(
|
||||||
|
new DynmapMapProvider(state.servers.get(serverName) as LiveAtlasServerDefinition));
|
||||||
|
break;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
//Sets the currently active map/world
|
//Sets the currently active map/world
|
||||||
@ -550,12 +565,12 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
//Set the follow target, which the map will automatically pan to keep in view
|
//Set the follow target, which the map will automatically pan to keep in view
|
||||||
[MutationTypes.SET_FOLLOW_TARGET](state: State, player: DynmapPlayer) {
|
[MutationTypes.SET_FOLLOW_TARGET](state: State, player: LiveAtlasPlayer) {
|
||||||
state.followTarget = player;
|
state.followTarget = player;
|
||||||
},
|
},
|
||||||
|
|
||||||
//Set the pan target, which the map will immediately pan to once
|
//Set the pan target, which the map will immediately pan to once
|
||||||
[MutationTypes.SET_PAN_TARGET](state: State, player: DynmapPlayer) {
|
[MutationTypes.SET_PAN_TARGET](state: State, player: LiveAtlasPlayer) {
|
||||||
state.panTarget = player;
|
state.panTarget = player;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,24 +1,22 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DynmapComponentConfig, DynmapMarkerSet, DynmapMarkerSetUpdates,
|
DynmapMarkerSetUpdates,
|
||||||
DynmapPlayer,
|
DynmapTileUpdate
|
||||||
DynmapServerConfig, DynmapTileUpdate,
|
|
||||||
DynmapChat
|
|
||||||
} from "@/dynmap";
|
} from "@/dynmap";
|
||||||
import {
|
import {
|
||||||
Coordinate,
|
Coordinate,
|
||||||
@ -29,37 +27,44 @@ import {
|
|||||||
LiveAtlasUIElement,
|
LiveAtlasUIElement,
|
||||||
LiveAtlasWorldDefinition,
|
LiveAtlasWorldDefinition,
|
||||||
LiveAtlasParsedUrl,
|
LiveAtlasParsedUrl,
|
||||||
LiveAtlasMessageConfig
|
LiveAtlasMessageConfig,
|
||||||
|
LiveAtlasMapProvider,
|
||||||
|
LiveAtlasPlayer,
|
||||||
|
LiveAtlasMarkerSet,
|
||||||
|
LiveAtlasComponentConfig,
|
||||||
|
LiveAtlasServerConfig, LiveAtlasChat
|
||||||
} from "@/index";
|
} from "@/index";
|
||||||
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
version: string;
|
version: string;
|
||||||
servers: Map<string, LiveAtlasServerDefinition>;
|
servers: Map<string, LiveAtlasServerDefinition>;
|
||||||
configuration: DynmapServerConfig;
|
configuration: LiveAtlasServerConfig;
|
||||||
configurationHash: number | undefined;
|
configurationHash: number | undefined;
|
||||||
messages: LiveAtlasMessageConfig;
|
messages: LiveAtlasMessageConfig;
|
||||||
components: DynmapComponentConfig;
|
components: LiveAtlasComponentConfig;
|
||||||
|
|
||||||
loggedIn: boolean;
|
loggedIn: boolean;
|
||||||
|
|
||||||
worlds: Map<string, LiveAtlasWorldDefinition>;
|
worlds: Map<string, LiveAtlasWorldDefinition>;
|
||||||
maps: Map<string, LiveAtlasMapDefinition>;
|
maps: Map<string, LiveAtlasMapDefinition>;
|
||||||
players: Map<string, DynmapPlayer>;
|
players: Map<string, LiveAtlasPlayer>;
|
||||||
sortedPlayers: LiveAtlasSortedPlayers;
|
sortedPlayers: LiveAtlasSortedPlayers;
|
||||||
markerSets: Map<string, DynmapMarkerSet>;
|
maxPlayers: number;
|
||||||
|
markerSets: Map<string, LiveAtlasMarkerSet>;
|
||||||
|
|
||||||
chat: {
|
chat: {
|
||||||
unread: number;
|
unread: number;
|
||||||
messages: DynmapChat[];
|
messages: LiveAtlasChat[];
|
||||||
};
|
};
|
||||||
|
|
||||||
pendingSetUpdates: Map<string, DynmapMarkerSetUpdates>;
|
pendingSetUpdates: Map<string, DynmapMarkerSetUpdates>;
|
||||||
pendingTileUpdates: Array<DynmapTileUpdate>;
|
pendingTileUpdates: Array<DynmapTileUpdate>;
|
||||||
|
|
||||||
followTarget?: DynmapPlayer;
|
followTarget?: LiveAtlasPlayer;
|
||||||
panTarget?: DynmapPlayer;
|
panTarget?: LiveAtlasPlayer;
|
||||||
|
|
||||||
|
currentMapProvider?: Readonly<LiveAtlasMapProvider>;
|
||||||
currentServer?: LiveAtlasServerDefinition;
|
currentServer?: LiveAtlasServerDefinition;
|
||||||
currentWorldState: LiveAtlasWorldState;
|
currentWorldState: LiveAtlasWorldState;
|
||||||
currentWorld?: LiveAtlasWorldDefinition;
|
currentWorld?: LiveAtlasWorldDefinition;
|
||||||
@ -67,9 +72,6 @@ export type State = {
|
|||||||
currentLocation: Coordinate;
|
currentLocation: Coordinate;
|
||||||
currentZoom: number;
|
currentZoom: number;
|
||||||
|
|
||||||
updateRequestId: number;
|
|
||||||
updateTimestamp: Date;
|
|
||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
playersAboveMarkers: boolean;
|
playersAboveMarkers: boolean;
|
||||||
playersSearch: boolean;
|
playersSearch: boolean;
|
||||||
@ -91,20 +93,13 @@ export const state: State = {
|
|||||||
servers: new Map(),
|
servers: new Map(),
|
||||||
|
|
||||||
configuration: {
|
configuration: {
|
||||||
version: '',
|
|
||||||
defaultMap: '',
|
defaultMap: '',
|
||||||
defaultWorld: '',
|
defaultWorld: '',
|
||||||
defaultZoom: 0,
|
defaultZoom: 0,
|
||||||
followMap: '',
|
followMap: '',
|
||||||
followZoom: 0,
|
followZoom: 0,
|
||||||
updateInterval: 3000,
|
|
||||||
showLayerControl: false,
|
|
||||||
title: '',
|
title: '',
|
||||||
loginEnabled: false,
|
|
||||||
maxPlayers: 0,
|
|
||||||
grayHiddenPlayers: false,
|
|
||||||
expandUI: false,
|
expandUI: false,
|
||||||
hash: 0,
|
|
||||||
},
|
},
|
||||||
configurationHash: undefined,
|
configurationHash: undefined,
|
||||||
|
|
||||||
@ -157,6 +152,7 @@ export const state: State = {
|
|||||||
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
|
||||||
sortedPlayers: [] as LiveAtlasSortedPlayers, //Online players from world.json, sorted by their sort property then alphabetically
|
sortedPlayers: [] as LiveAtlasSortedPlayers, //Online players from world.json, sorted by their sort property then alphabetically
|
||||||
|
maxPlayers: 0,
|
||||||
|
|
||||||
chat: {
|
chat: {
|
||||||
unread: 0,
|
unread: 0,
|
||||||
@ -187,6 +183,9 @@ export const state: State = {
|
|||||||
//Optional "link" component. Adds button to copy url for current position
|
//Optional "link" component. Adds button to copy url for current position
|
||||||
linkControl: false,
|
linkControl: false,
|
||||||
|
|
||||||
|
//Layers control
|
||||||
|
layerControl: false,
|
||||||
|
|
||||||
//Optional "logo" controls.
|
//Optional "logo" controls.
|
||||||
logoControls: [],
|
logoControls: [],
|
||||||
|
|
||||||
@ -197,12 +196,16 @@ export const state: State = {
|
|||||||
chatBox: undefined,
|
chatBox: undefined,
|
||||||
|
|
||||||
//Chat balloons showing messages above player markers
|
//Chat balloons showing messages above player markers
|
||||||
chatBalloons: false
|
chatBalloons: false,
|
||||||
|
|
||||||
|
//Login/registering (not currently implemented)
|
||||||
|
login: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
followTarget: undefined,
|
followTarget: undefined,
|
||||||
panTarget: undefined,
|
panTarget: undefined,
|
||||||
|
|
||||||
|
currentMapProvider: undefined,
|
||||||
currentServer: undefined,
|
currentServer: undefined,
|
||||||
currentWorld: undefined,
|
currentWorld: undefined,
|
||||||
currentMap: undefined,
|
currentMap: undefined,
|
||||||
@ -218,9 +221,6 @@ export const state: State = {
|
|||||||
timeOfDay: 0,
|
timeOfDay: 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
updateRequestId: 0,
|
|
||||||
updateTimestamp: new Date(),
|
|
||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
playersAboveMarkers: true,
|
playersAboveMarkers: true,
|
||||||
playersSearch: true,
|
playersSearch: true,
|
||||||
|
79
src/util.ts
79
src/util.ts
@ -1,30 +1,22 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import API from '@/api';
|
|
||||||
import {DynmapPlayer} from "@/dynmap";
|
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
|
||||||
|
import {HeadQueueEntry, LiveAtlasPlayer} from "@/index";
|
||||||
interface HeadQueueEntry {
|
|
||||||
cacheKey: string;
|
|
||||||
account: string;
|
|
||||||
size: string;
|
|
||||||
image: HTMLImageElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
const headCache = new Map<string, HTMLImageElement>(),
|
const headCache = new Map<string, HTMLImageElement>(),
|
||||||
headUnresolvedCache = new Map<string, Promise<HTMLImageElement>>(),
|
headUnresolvedCache = new Map<string, Promise<HTMLImageElement>>(),
|
||||||
@ -32,6 +24,10 @@ const headCache = new Map<string, HTMLImageElement>(),
|
|||||||
|
|
||||||
headQueue: HeadQueueEntry[] = [];
|
headQueue: HeadQueueEntry[] = [];
|
||||||
|
|
||||||
|
export const titleColoursRegex = /§[0-9a-f]/ig;
|
||||||
|
export const netherWorldNameRegex = /_?nether(_|$)/i;
|
||||||
|
export const endWorldNameRegex = /(^|_)end(_|$)/i;
|
||||||
|
|
||||||
export const getMinecraftTime = (serverTime: number) => {
|
export const getMinecraftTime = (serverTime: number) => {
|
||||||
const day = serverTime >= 0 && serverTime < 13700;
|
const day = serverTime >= 0 && serverTime < 13700;
|
||||||
|
|
||||||
@ -49,8 +45,9 @@ export const getMinecraftTime = (serverTime: number) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getMinecraftHead = (player: DynmapPlayer | string, size: string): Promise<HTMLImageElement> => {
|
export const getMinecraftHead = (player: LiveAtlasPlayer | string, size: string): Promise<HTMLImageElement> => {
|
||||||
const account = typeof player === 'string' ? player : player.account,
|
const account = typeof player === 'string' ? player : player.name,
|
||||||
|
uuid = typeof player === 'string' ? undefined : player.uuid,
|
||||||
cacheKey = `${account}-${size}`;
|
cacheKey = `${account}-${size}`;
|
||||||
|
|
||||||
if(headCache.has(cacheKey)) {
|
if(headCache.has(cacheKey)) {
|
||||||
@ -79,7 +76,8 @@ export const getMinecraftHead = (player: DynmapPlayer | string, size: string): P
|
|||||||
};
|
};
|
||||||
|
|
||||||
headQueue.push({
|
headQueue.push({
|
||||||
account,
|
name: account,
|
||||||
|
uuid,
|
||||||
size,
|
size,
|
||||||
cacheKey,
|
cacheKey,
|
||||||
image: faceImage,
|
image: faceImage,
|
||||||
@ -97,37 +95,14 @@ const tickHeadQueue = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const head = headQueue.pop() as HeadQueueEntry,
|
const head = headQueue.pop() as HeadQueueEntry;
|
||||||
src = (head.size === 'body') ? `faces/body/${head.account}.png` :`faces/${head.size}x${head.size}/${head.account}.png`;
|
|
||||||
|
|
||||||
headsLoading.add(head.cacheKey);
|
headsLoading.add(head.cacheKey);
|
||||||
head.image.src = concatURL(useStore().getters.serverConfig.dynmap.markers, src);
|
head.image.src = useStore().state.currentMapProvider!.getPlayerHeadUrl(head);
|
||||||
|
|
||||||
tickHeadQueue();
|
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;
|
|
||||||
|
|
||||||
if(map) {
|
|
||||||
return (x: number, y: number, z: number) => {
|
|
||||||
return map.locationToLatLng({x, y, z});
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return (x: number, y: number, z: number) => {
|
|
||||||
return LiveAtlasMapDefinition.defaultProjection.locationToLatLng({x, y, z});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const parseUrl = () => {
|
export const parseUrl = () => {
|
||||||
const query = new URLSearchParams(window.location.search),
|
const query = new URLSearchParams(window.location.search),
|
||||||
hash = window.location.hash.replace('#', '');
|
hash = window.location.hash.replace('#', '');
|
||||||
@ -211,16 +186,6 @@ export const parseMapSearchParams = (query: URLSearchParams) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAPI = () => {
|
|
||||||
const store = useStore();
|
|
||||||
|
|
||||||
if(!store.state.currentServer) {
|
|
||||||
throw new RangeError("No current server");
|
|
||||||
}
|
|
||||||
|
|
||||||
return API;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getUrlForLocation = (map: LiveAtlasMapDefinition, location: {
|
export const getUrlForLocation = (map: LiveAtlasMapDefinition, location: {
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
|
@ -1,56 +1,52 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
||||||
* These portions are Copyright 2020 Dynmap Contributors.
|
* These portions are Copyright 2020 Dynmap Contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {LatLngExpression} from "leaflet";
|
import {LatLngExpression} from "leaflet";
|
||||||
import {DynmapArea} from "@/dynmap";
|
|
||||||
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
|
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
|
||||||
import LiveAtlasPolygon from "@/leaflet/vector/LiveAtlasPolygon";
|
import LiveAtlasPolygon from "@/leaflet/vector/LiveAtlasPolygon";
|
||||||
|
import {Coordinate, LiveAtlasArea} from "@/index";
|
||||||
|
import {arePointsEqual, createPopup, isStyleEqual, tooltipOptions} from "@/util/paths";
|
||||||
|
|
||||||
export const createArea = (options: DynmapArea, converter: Function): LiveAtlasPolyline | LiveAtlasPolygon => {
|
export const createArea = (options: LiveAtlasArea, converter: Function): LiveAtlasPolyline | LiveAtlasPolygon => {
|
||||||
const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0),
|
const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0),
|
||||||
points = getPoints(options, converter, outline),
|
points = options.points.map(projectPointsMapCallback, converter) as LatLngExpression[] | LatLngExpression[][],
|
||||||
area = outline ? new LiveAtlasPolyline(points, {
|
area = outline ? new LiveAtlasPolyline(points, options) : new LiveAtlasPolygon(points, options);
|
||||||
...options.style,
|
|
||||||
minZoom: options.minZoom,
|
|
||||||
maxZoom: options.maxZoom,
|
|
||||||
}) : new LiveAtlasPolygon(points, {
|
|
||||||
...options.style,
|
|
||||||
minZoom: options.minZoom,
|
|
||||||
maxZoom: options.maxZoom,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (options.label) {
|
if (options.popupContent) {
|
||||||
area.bindPopup(() => createPopup(options));
|
area.bindPopup(() => createPopup(options, 'AreaPopup'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.tooltipContent) {
|
||||||
|
area.bindTooltip(() => options.tooltipContent as string, tooltipOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
return area;
|
return area;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateArea = (area: LiveAtlasPolyline | LiveAtlasPolygon | undefined, options: DynmapArea, converter: Function): LiveAtlasPolyline | LiveAtlasPolygon => {
|
export const updateArea = (area: LiveAtlasPolyline | LiveAtlasPolygon | undefined, options: LiveAtlasArea, converter: Function): LiveAtlasPolyline | LiveAtlasPolygon => {
|
||||||
const outline = !options.style || !options.style.fillOpacity || (options.style.fillOpacity <= 0) as boolean,
|
|
||||||
points = getPoints(options, converter, outline);
|
|
||||||
|
|
||||||
if (!area) {
|
if (!area) {
|
||||||
return createArea(options, converter);
|
return createArea(options, converter);
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldPoints = area.getLatLngs();
|
const points = options.points.map(projectPointsMapCallback, converter) as LatLngExpression[] | LatLngExpression[][],
|
||||||
|
oldPoints = area.getLatLngs();
|
||||||
|
|
||||||
let dirty = false;
|
let dirty = false;
|
||||||
|
|
||||||
//Avoid pointless setStyle() redrawing by checking if styles have actually changed
|
//Avoid pointless setStyle() redrawing by checking if styles have actually changed
|
||||||
@ -66,7 +62,7 @@ export const updateArea = (area: LiveAtlasPolyline | LiveAtlasPolygon | undefine
|
|||||||
|
|
||||||
area.closePopup();
|
area.closePopup();
|
||||||
area.unbindPopup();
|
area.unbindPopup();
|
||||||
area.bindPopup(() => createPopup(options));
|
area.bindPopup(() => createPopup(options, 'AreaPopup'));
|
||||||
|
|
||||||
if(dirty) {
|
if(dirty) {
|
||||||
area.redraw();
|
area.redraw();
|
||||||
@ -75,149 +71,129 @@ export const updateArea = (area: LiveAtlasPolyline | LiveAtlasPolygon | undefine
|
|||||||
return area;
|
return area;
|
||||||
};
|
};
|
||||||
|
|
||||||
const arePointsEqual = (oldPoints: any, newPoints: any) => {
|
const projectPointsMapCallback = function(this: Function, point: Coordinate | Coordinate[] | Coordinate[][]): LatLngExpression | LatLngExpression[] {
|
||||||
return JSON.stringify(oldPoints) === JSON.stringify(newPoints);
|
if(Array.isArray(point)) {
|
||||||
}
|
return point.map(projectPointsMapCallback, this) as LatLngExpression[];
|
||||||
|
|
||||||
const isStyleEqual = (oldStyle: any, newStyle: any) => {
|
|
||||||
return oldStyle && newStyle
|
|
||||||
&& (oldStyle.color === newStyle.color)
|
|
||||||
&& (oldStyle.weight === newStyle.weight)
|
|
||||||
&& (oldStyle.opacity === newStyle.opacity)
|
|
||||||
&& (oldStyle.fillColor === newStyle.fillColor)
|
|
||||||
&& (oldStyle.fillOpacity === newStyle.fillOpacity)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
} else {
|
||||||
popup.textContent = options.label;
|
// @ts-ignore
|
||||||
|
return this(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
return popup;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPoints = (options: DynmapArea, converter: Function, outline: boolean): LatLngExpression[] | LatLngExpression[][] => {
|
export const getPoints = (x: number[], y: [number, number], z: number[], outline: boolean): Coordinate[] | Coordinate[][] => {
|
||||||
if (options.x.length === 2) { /* Only 2 points */
|
if (x.length === 2) { /* Only 2 points */
|
||||||
if (options.y[0] === options.y[1]) {
|
if (y[0] === y[1]) {
|
||||||
return get2DBoxPoints(options, converter, outline);
|
return get2DBoxPoints(x, y, z, outline);
|
||||||
} else {
|
} else {
|
||||||
return get3DBoxPoints(options, converter);
|
return get3DBoxPoints(x, y, z);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (options.y[0] === options.y[1]) {
|
if (y[0] === y[1]) {
|
||||||
return get2DShapePoints(options, converter, outline);
|
return get2DShapePoints(x, y, z, outline);
|
||||||
} else {
|
} else {
|
||||||
return get3DShapePoints(options, converter);
|
return get3DShapePoints(x, y, z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const get3DBoxPoints = (options: DynmapArea, converter: Function): LatLngExpression[][] => {
|
export const get3DBoxPoints = (x: number[], y: [number, number], z: number[]): Coordinate[][] => {
|
||||||
const maxX = options.x[0],
|
const maxX = x[0],
|
||||||
minX = options.x[1],
|
minX = x[1],
|
||||||
maxY = options.y[0],
|
maxY = y[0],
|
||||||
minY = options.y[1],
|
minY = y[1],
|
||||||
maxZ = options.z[0],
|
maxZ = z[0],
|
||||||
minZ = options.z[1];
|
minZ = z[1];
|
||||||
|
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
converter(minX, minY, minZ),
|
{x: minX, y: minY, z: minZ},
|
||||||
converter(maxX, minY, minZ),
|
{x: maxX, y: minY, z: minZ},
|
||||||
converter(maxX, minY, maxZ),
|
{x: maxX, y: minY, z: maxZ},
|
||||||
converter(minX, minY, maxZ)
|
{x: minX, y: minY, z: maxZ}
|
||||||
], [
|
], [
|
||||||
converter(minX, maxY, minZ),
|
{x: minX, y: maxY, z: minZ},
|
||||||
converter(maxX, maxY, minZ),
|
{x: maxX, y: maxY, z: minZ},
|
||||||
converter(maxX, maxY, maxZ),
|
{x: maxX, y: maxY, z: maxZ},
|
||||||
converter(minX, maxY, maxZ)
|
{x: minX, y: maxY, z: maxZ}
|
||||||
], [
|
], [
|
||||||
converter(minX, minY, minZ),
|
{x: minX, y: minY, z: minZ},
|
||||||
converter(minX, maxY, minZ),
|
{x: minX, y: maxY, z: minZ},
|
||||||
converter(maxX, maxY, minZ),
|
{x: maxX, y: maxY, z: minZ},
|
||||||
converter(maxX, minY, minZ)
|
{x: maxX, y: minY, z: minZ}
|
||||||
], [
|
], [
|
||||||
converter(maxX, minY, minZ),
|
{x: maxX, y: minY, z: minZ},
|
||||||
converter(maxX, maxY, minZ),
|
{x: maxX, y: maxY, z: minZ},
|
||||||
converter(maxX, maxY, maxZ),
|
{x: maxX, y: maxY, z: maxZ},
|
||||||
converter(maxX, minY, maxZ)
|
{x: maxX, y: minY, z: maxZ}
|
||||||
], [
|
], [
|
||||||
converter(minX, minY, maxZ),
|
{x: minX, y: minY, z: maxZ},
|
||||||
converter(minX, maxY, maxZ),
|
{x: minX, y: maxY, z: maxZ},
|
||||||
converter(maxX, maxY, maxZ),
|
{x: maxX, y: maxY, z: maxZ},
|
||||||
converter(maxX, minY, maxZ)
|
{x: maxX, y: minY, z: maxZ}
|
||||||
], [
|
], [
|
||||||
converter(minX, minY, minZ),
|
{x: minX, y: minY, z: minZ},
|
||||||
converter(minX, maxY, minZ),
|
{x: minX, y: maxY, z: minZ},
|
||||||
converter(minX, maxY, maxZ),
|
{x: minX, y: maxY, z: maxZ},
|
||||||
converter(minX, minY, maxZ)
|
{x: minX, y: minY, z: maxZ}
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const get2DBoxPoints = (options: DynmapArea, converter: Function, outline: boolean): LatLngExpression[] => {
|
export const get2DBoxPoints = (x: number[], y: [number, number], z: number[], outline: boolean): Coordinate[] => {
|
||||||
const maxX = options.x[0],
|
const maxX = x[0],
|
||||||
minX = options.x[1],
|
minX = x[1],
|
||||||
minY = options.y[1],
|
minY = y[1],
|
||||||
maxZ = options.z[0],
|
maxZ = z[0],
|
||||||
minZ = options.z[1];
|
minZ = z[1];
|
||||||
|
|
||||||
if (outline) {
|
if (outline) {
|
||||||
return [
|
return [
|
||||||
converter(minX, minY, minZ),
|
{x: minX, y: minY, z: minZ},
|
||||||
converter(maxX, minY, minZ),
|
{x: maxX, y: minY, z: minZ},
|
||||||
converter(maxX, minY, maxZ),
|
{x: maxX, y: minY, z: maxZ},
|
||||||
converter(minX, minY, maxZ),
|
{x: minX, y: minY, z: maxZ},
|
||||||
converter(minX, minY, minZ)
|
{x: minX, y: minY, z: minZ}
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
return [
|
return [
|
||||||
converter(minX, minY, minZ),
|
{x: minX, y: minY, z: minZ},
|
||||||
converter(maxX, minY, minZ),
|
{x: maxX, y: minY, z: minZ},
|
||||||
converter(maxX, minY, maxZ),
|
{x: maxX, y: minY, z: maxZ},
|
||||||
converter(minX, minY, maxZ)
|
{x: minX, y: minY, z: maxZ}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const get3DShapePoints = (options: DynmapArea, converter: Function): LatLngExpression[][] => {
|
export const get3DShapePoints = (x: number[], y: [number, number], z: number[]): Coordinate[][] => {
|
||||||
const toplist = [],
|
const toplist = [],
|
||||||
botlist = [],
|
botlist = [],
|
||||||
polylist = [];
|
polylist = [];
|
||||||
|
|
||||||
for (let i = 0; i < options.x.length; i++) {
|
for (let i = 0; i < x.length; i++) {
|
||||||
toplist[i] = converter(options.x[i], options.y[0], options.z[i]);
|
toplist[i] = {x: x[i], y: y[0], z: z[i]};
|
||||||
botlist[i] = converter(options.x[i], options.y[1], options.z[i]);
|
botlist[i] = {x: x[i], y: y[1], z: z[i]};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < options.x.length; i++) {
|
for (let i = 0; i < x.length; i++) {
|
||||||
const sidelist = [];
|
polylist[i] = [
|
||||||
sidelist[0] = toplist[i];
|
toplist[i],
|
||||||
sidelist[1] = botlist[i];
|
botlist[i],
|
||||||
sidelist[2] = botlist[(i + 1) % options.x.length];
|
botlist[(i + 1) % x.length],
|
||||||
sidelist[3] = toplist[(i + 1) % options.x.length];
|
toplist[(i + 1) % x.length],
|
||||||
polylist[i] = sidelist;
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
polylist[options.x.length] = botlist;
|
polylist[x.length] = botlist;
|
||||||
polylist[options.x.length + 1] = toplist;
|
polylist[x.length + 1] = toplist;
|
||||||
|
|
||||||
return polylist;
|
return polylist;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const get2DShapePoints = (options: DynmapArea, converter: Function, outline: boolean): LatLngExpression[] => {
|
export const get2DShapePoints = (x: number[], y: [number, number], z: number[], outline: boolean): Coordinate[] => {
|
||||||
const points = [];
|
const points = [];
|
||||||
|
|
||||||
for (let i = 0; i < options.x.length; i++) {
|
for (let i = 0; i < x.length; i++) {
|
||||||
points[i] = converter(options.x[i], options.y[1], options.z[i]);
|
points[i] = {x: x[i], y: y[1], z: z[i]};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outline) {
|
if (outline) {
|
||||||
@ -225,4 +201,4 @@ export const get2DShapePoints = (options: DynmapArea, converter: Function, outli
|
|||||||
}
|
}
|
||||||
|
|
||||||
return points;
|
return points;
|
||||||
}
|
};
|
||||||
|
@ -1,82 +1,62 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
||||||
* These portions are Copyright 2020 Dynmap Contributors.
|
* These portions are Copyright 2020 Dynmap Contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {DynmapCircle} from "@/dynmap";
|
|
||||||
import {LatLngExpression} from "leaflet";
|
import {LatLngExpression} from "leaflet";
|
||||||
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
|
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
|
||||||
import LiveAtlasPolygon from "@/leaflet/vector/LiveAtlasPolygon";
|
import LiveAtlasPolygon from "@/leaflet/vector/LiveAtlasPolygon";
|
||||||
|
import {LiveAtlasCircle} from "@/index";
|
||||||
|
import {createPopup, tooltipOptions} from "@/util/paths";
|
||||||
|
|
||||||
export const createCircle = (options: DynmapCircle, converter: Function): LiveAtlasPolyline | LiveAtlasPolygon => {
|
export const createCircle = (options: LiveAtlasCircle, converter: Function): LiveAtlasPolyline | LiveAtlasPolygon => {
|
||||||
const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0),
|
const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0),
|
||||||
points = getCirclePoints(options, converter, outline),
|
points = getCirclePoints(options, converter, outline),
|
||||||
circle = outline ? new LiveAtlasPolyline(points, {
|
circle = outline ? new LiveAtlasPolyline(points, options) : new LiveAtlasPolygon(points, options);
|
||||||
...options.style,
|
|
||||||
minZoom: options.minZoom,
|
|
||||||
maxZoom: options.maxZoom,
|
|
||||||
}) : new LiveAtlasPolygon(points, {
|
|
||||||
...options.style,
|
|
||||||
minZoom: options.minZoom,
|
|
||||||
maxZoom: options.maxZoom,
|
|
||||||
});
|
|
||||||
|
|
||||||
if(options.label) {
|
if(options.popupContent) {
|
||||||
circle.bindPopup(() => createPopup(options));
|
circle.bindPopup(() => createPopup(options, 'CirclePopup'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.tooltipContent) {
|
||||||
|
circle.bindTooltip(() => options.tooltipContent as string, tooltipOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
return circle;
|
return circle;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateCircle = (circle: LiveAtlasPolyline | LiveAtlasPolygon | undefined, options: DynmapCircle, converter: Function): LiveAtlasPolyline | LiveAtlasPolygon => {
|
export const updateCircle = (circle: LiveAtlasPolyline | LiveAtlasPolygon | undefined, options: LiveAtlasCircle, converter: Function): LiveAtlasPolyline | LiveAtlasPolygon => {
|
||||||
const outline = (options.style && options.style.fillOpacity && (options.style.fillOpacity <= 0)) as boolean,
|
|
||||||
points = getCirclePoints(options, converter, outline);
|
|
||||||
|
|
||||||
if (!circle) {
|
if (!circle) {
|
||||||
return createCircle(options, converter);
|
return createCircle(options, converter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const outline = (options.style && options.style.fillOpacity && (options.style.fillOpacity <= 0)) as boolean;
|
||||||
|
|
||||||
circle.closePopup();
|
circle.closePopup();
|
||||||
circle.unbindPopup();
|
circle.unbindPopup();
|
||||||
circle.bindPopup(() => createPopup(options));
|
circle.bindPopup(() => createPopup(options, 'CirclePopup'));
|
||||||
circle.setStyle(options.style);
|
circle.setStyle(options.style);
|
||||||
circle.setLatLngs(points);
|
circle.setLatLngs(getCirclePoints(options, converter, outline));
|
||||||
circle.redraw();
|
circle.redraw();
|
||||||
|
|
||||||
return circle;
|
return circle;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createPopup = (options: DynmapCircle) => {
|
export const getCirclePoints = (options: LiveAtlasCircle, converter: Function, outline: boolean): LatLngExpression[] => {
|
||||||
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 = [];
|
const points = [];
|
||||||
|
|
||||||
for(let i = 0; i < 360; i++) {
|
for(let i = 0; i < 360; i++) {
|
||||||
@ -84,7 +64,7 @@ export const getCirclePoints = (options: DynmapCircle, converter: Function, outl
|
|||||||
x = options.radius[0] * Math.sin(rad) + options.location.x,
|
x = options.radius[0] * Math.sin(rad) + options.location.x,
|
||||||
z = options.radius[1] * Math.cos(rad) + options.location.z;
|
z = options.radius[1] * Math.cos(rad) + options.location.z;
|
||||||
|
|
||||||
points.push(converter(x, options.location.y, z));
|
points.push(converter({x, y:options.location.y, z}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(outline && points.length) {
|
if(outline && points.length) {
|
||||||
|
@ -1,4 +1,20 @@
|
|||||||
import {LiveAtlasDynmapServerDefinition, LiveAtlasGlobalConfig, LiveAtlasServerDefinition} from "@/index";
|
/*
|
||||||
|
* Copyright 2021 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 {LiveAtlasGlobalConfig, LiveAtlasServerDefinition} from "@/index";
|
||||||
import ConfigurationError from "@/errors/ConfigurationError";
|
import ConfigurationError from "@/errors/ConfigurationError";
|
||||||
import {DynmapUrlConfig} from "@/dynmap";
|
import {DynmapUrlConfig} from "@/dynmap";
|
||||||
|
|
||||||
@ -22,40 +38,37 @@ const validateLiveAtlasConfiguration = (config: any): Map<string, LiveAtlasServe
|
|||||||
}
|
}
|
||||||
|
|
||||||
serverConfig.id = server;
|
serverConfig.id = server;
|
||||||
serverConfig.type = serverConfig.type || 'dynmap';
|
|
||||||
|
|
||||||
switch(serverConfig.type) {
|
if(typeof serverConfig.pl3xmap !== 'undefined') {
|
||||||
case 'dynmap':
|
serverConfig.type = 'pl3xmap';
|
||||||
if (!serverConfig.dynmap || serverConfig.dynmap.constructor !== Object) {
|
} else if(typeof serverConfig.dynmap !== 'undefined') {
|
||||||
throw new ConfigurationError(`Server '${server}': Dynmap configuration object missing. ${check}`);
|
if (!serverConfig.dynmap || serverConfig.dynmap.constructor !== Object) {
|
||||||
}
|
throw new ConfigurationError(`Server '${server}': Dynmap configuration object missing. ${check}`);
|
||||||
|
}
|
||||||
|
|
||||||
if (!serverConfig.dynmap.configuration) {
|
if (!serverConfig.dynmap.configuration) {
|
||||||
throw new ConfigurationError(`Server '${server}': Dynmap configuration URL missing. ${check}`);
|
throw new ConfigurationError(`Server '${server}': Dynmap configuration URL missing. ${check}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!serverConfig.dynmap.update) {
|
if (!serverConfig.dynmap.update) {
|
||||||
throw new ConfigurationError(`Server '${server}': Dynmap update URL missing. ${check}`);
|
throw new ConfigurationError(`Server '${server}': Dynmap update URL missing. ${check}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!serverConfig.dynmap.markers) {
|
if (!serverConfig.dynmap.markers) {
|
||||||
throw new ConfigurationError(`Server '${server}': Dynmap markers URL missing. ${check}`);
|
throw new ConfigurationError(`Server '${server}': Dynmap markers URL missing. ${check}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!serverConfig.dynmap.tiles) {
|
if (!serverConfig.dynmap.tiles) {
|
||||||
throw new ConfigurationError(`Server '${server}': Dynmap tiles URL missing. ${check}`);
|
throw new ConfigurationError(`Server '${server}': Dynmap tiles URL missing. ${check}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!serverConfig.dynmap.sendmessage) {
|
if (!serverConfig.dynmap.sendmessage) {
|
||||||
throw new ConfigurationError(`Server '${server}': Dynmap sendmessage URL missing. ${check}`);
|
throw new ConfigurationError(`Server '${server}': Dynmap sendmessage URL missing. ${check}`);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case 'pl3xmap':
|
serverConfig.type = 'dynmap';
|
||||||
case 'plexmap':
|
} else {
|
||||||
if (!serverConfig.plexmap || serverConfig.plexmap.constructor !== Object) {
|
throw new ConfigurationError(`Server '${server}': No Dynmap or Pl3xmap configuration defined. ${check}`);
|
||||||
throw new ConfigurationError(`Server '${server}': Pl3xmap configuration object missing. ${check}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.set(server, serverConfig);
|
result.set(server, serverConfig);
|
||||||
@ -64,7 +77,7 @@ const validateLiveAtlasConfiguration = (config: any): Map<string, LiveAtlasServe
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateDynmapConfiguration = (config: DynmapUrlConfig): Map<string, LiveAtlasDynmapServerDefinition> => {
|
const validateDynmapConfiguration = (config: DynmapUrlConfig): Map<string, LiveAtlasServerDefinition> => {
|
||||||
const check = '\nCheck your standalone/config.js file exists and is being loaded correctly.';
|
const check = '\nCheck your standalone/config.js file exists and is being loaded correctly.';
|
||||||
|
|
||||||
if (!config) {
|
if (!config) {
|
||||||
@ -91,7 +104,7 @@ const validateDynmapConfiguration = (config: DynmapUrlConfig): Map<string, LiveA
|
|||||||
throw new ConfigurationError(`Dynmap sendmessage URL is missing. ${check}`);
|
throw new ConfigurationError(`Dynmap sendmessage URL is missing. ${check}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = new Map<string, LiveAtlasDynmapServerDefinition>();
|
const result = new Map<string, LiveAtlasServerDefinition>();
|
||||||
result.set('dynmap', {
|
result.set('dynmap', {
|
||||||
id: 'dynmap',
|
id: 'dynmap',
|
||||||
label: 'dynmap',
|
label: 'dynmap',
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
const navigationKeys = new Set<string>([
|
const navigationKeys = new Set<string>([
|
||||||
'ArrowUp',
|
'ArrowUp',
|
||||||
'ArrowDown',
|
'ArrowDown',
|
||||||
@ -61,4 +77,4 @@ export const handleKeyboardEvent = (e: KeyboardEvent, elements: HTMLElement[]) =
|
|||||||
e.target.dispatchEvent(mouseEvent);
|
e.target.dispatchEvent(mouseEvent);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,79 +1,71 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
||||||
* These portions are Copyright 2020 Dynmap Contributors.
|
* These portions are Copyright 2020 Dynmap Contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {DynmapLine} from "@/dynmap";
|
|
||||||
import {LatLngExpression} from "leaflet";
|
|
||||||
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
|
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
|
||||||
|
import {Coordinate, LiveAtlasLine} from "@/index";
|
||||||
|
import {LatLngExpression} from "leaflet";
|
||||||
|
import {createPopup, tooltipOptions} from "@/util/paths";
|
||||||
|
|
||||||
export const createLine = (options: DynmapLine, converter: Function): LiveAtlasPolyline => {
|
export const createLine = (options: LiveAtlasLine, converter: Function): LiveAtlasPolyline => {
|
||||||
const points = getLinePoints(options, converter),
|
const points = options.points.map(projectPointsMapCallback, converter),
|
||||||
line = new LiveAtlasPolyline(points, {
|
line = new LiveAtlasPolyline(points, options);
|
||||||
...options.style,
|
|
||||||
minZoom: options.minZoom,
|
|
||||||
maxZoom: options.maxZoom,
|
|
||||||
});
|
|
||||||
|
|
||||||
if(options.label) {
|
if(options.popupContent) {
|
||||||
line.bindPopup(() => createPopup(options));
|
line.bindPopup(() => createPopup(options, 'LinePopup'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.tooltipContent) {
|
||||||
|
line.bindTooltip(() => options.tooltipContent as string, tooltipOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
return line;
|
return line;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateLine = (line: LiveAtlasPolyline | undefined, options: DynmapLine, converter: Function): LiveAtlasPolyline => {
|
export const updateLine = (line: LiveAtlasPolyline | undefined, options: LiveAtlasLine, converter: Function): LiveAtlasPolyline => {
|
||||||
const points = getLinePoints(options, converter);
|
|
||||||
|
|
||||||
if (!line) {
|
if (!line) {
|
||||||
return createLine(options, converter);
|
return createLine(options, converter);
|
||||||
}
|
}
|
||||||
|
|
||||||
line.closePopup();
|
line.closePopup();
|
||||||
line.unbindPopup();
|
line.unbindPopup();
|
||||||
line.bindPopup(() => createPopup(options));
|
line.bindPopup(() => createPopup(options, 'LinePopup'));
|
||||||
line.setStyle(options.style);
|
line.setStyle(options.style);
|
||||||
line.setLatLngs(points);
|
line.setLatLngs(options.points.map(projectPointsMapCallback, converter));
|
||||||
line.redraw();
|
line.redraw();
|
||||||
|
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createPopup = (options: DynmapLine) => {
|
const projectPointsMapCallback = function(point: Coordinate): LatLngExpression {
|
||||||
const popup = document.createElement('span');
|
if(Array.isArray(point)) {
|
||||||
|
return projectPointsMapCallback(point);
|
||||||
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 {
|
} else {
|
||||||
popup.textContent = options.label;
|
// @ts-ignore
|
||||||
|
return this(point);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return popup;
|
export const getLinePoints = (x: number[], y: number[], z: number[]): Coordinate[] => {
|
||||||
}
|
|
||||||
|
|
||||||
export const getLinePoints = (options: DynmapLine, converter: Function): LatLngExpression[] => {
|
|
||||||
const points = [];
|
const points = [];
|
||||||
|
|
||||||
for(let i = 0; i < options.x.length; i++) {
|
for(let i = 0; i < x.length; i++) {
|
||||||
points.push(converter(options.x[i], options.y[i], options.z[i]));
|
points.push({x: x[i], y: y[i], z: z[i]});
|
||||||
}
|
}
|
||||||
|
|
||||||
return points;
|
return points;
|
||||||
|
@ -1,38 +1,29 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 James Lyne
|
* Copyright 2021 James Lyne
|
||||||
*
|
*
|
||||||
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
|
||||||
* These portions are Copyright 2020 Dynmap Contributors.
|
* These portions are Copyright 2020 Dynmap Contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {LeafletMouseEvent, Marker} from "leaflet";
|
import {LeafletMouseEvent, Marker} from "leaflet";
|
||||||
import {DynmapMarker} from "@/dynmap";
|
|
||||||
import {GenericIcon} from "@/leaflet/icon/GenericIcon";
|
import {GenericIcon} from "@/leaflet/icon/GenericIcon";
|
||||||
import {GenericMarker} from "@/leaflet/marker/GenericMarker";
|
import {GenericMarker} from "@/leaflet/marker/GenericMarker";
|
||||||
|
import {LiveAtlasMarker} from "@/index";
|
||||||
|
|
||||||
export const createMarker = (options: DynmapMarker, converter: Function): Marker => {
|
export const createMarker = (options: LiveAtlasMarker, converter: Function): Marker => {
|
||||||
const marker = new GenericMarker(converter(options.location.x, options.location.y, options.location.z), {
|
const marker = new GenericMarker(converter(options.location), options);
|
||||||
icon: new GenericIcon({
|
|
||||||
icon: options.icon,
|
|
||||||
label: options.label,
|
|
||||||
iconSize: options.dimensions,
|
|
||||||
isHtml: options.isHTML,
|
|
||||||
}),
|
|
||||||
maxZoom: options.maxZoom,
|
|
||||||
minZoom: options.minZoom,
|
|
||||||
});
|
|
||||||
|
|
||||||
marker.on('click', (e: LeafletMouseEvent) => {
|
marker.on('click', (e: LeafletMouseEvent) => {
|
||||||
e.target._map.panTo(e.target.getLatLng());
|
e.target._map.panTo(e.target.getLatLng());
|
||||||
@ -45,13 +36,13 @@ export const createMarker = (options: DynmapMarker, converter: Function): Marker
|
|||||||
return marker;
|
return marker;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateMarker = (marker: Marker | undefined, options: DynmapMarker, converter: Function): Marker => {
|
export const updateMarker = (marker: Marker | undefined, options: LiveAtlasMarker, converter: Function): Marker => {
|
||||||
if (!marker) {
|
if (!marker) {
|
||||||
return createMarker(options, converter);
|
return createMarker(options, converter);
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldLocation = marker.getLatLng(),
|
const oldLocation = marker.getLatLng(),
|
||||||
newLocation = converter(options.location.x, options.location.y, options.location.z);
|
newLocation = converter(options.location);
|
||||||
|
|
||||||
if(!oldLocation.equals(newLocation)) {
|
if(!oldLocation.equals(newLocation)) {
|
||||||
marker.setLatLng(newLocation);
|
marker.setLatLng(newLocation);
|
||||||
@ -65,7 +56,7 @@ export const updateMarker = (marker: Marker | undefined, options: DynmapMarker,
|
|||||||
icon: options.icon,
|
icon: options.icon,
|
||||||
label: options.label,
|
label: options.label,
|
||||||
iconSize: options.dimensions,
|
iconSize: options.dimensions,
|
||||||
isHtml: options.isHTML,
|
isHtml: options.isLabelHTML,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,17 +71,12 @@ export const updateMarker = (marker: Marker | undefined, options: DynmapMarker,
|
|||||||
return marker;
|
return marker;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createPopup = (options: DynmapMarker) => {
|
const createPopup = (options: LiveAtlasMarker) => {
|
||||||
const popup = document.createElement('span');
|
const popup = document.createElement('span');
|
||||||
|
|
||||||
if (options.popupContent) {
|
if (options.popupContent) {
|
||||||
popup.classList.add('MarkerPopup');
|
popup.classList.add('MarkerPopup');
|
||||||
popup.insertAdjacentHTML('afterbegin', options.popupContent);
|
popup.insertAdjacentHTML('afterbegin', options.popupContent);
|
||||||
} else if (options.isHTML) {
|
|
||||||
popup.classList.add('MarkerPopup');
|
|
||||||
popup.insertAdjacentHTML('afterbegin', options.label);
|
|
||||||
} else {
|
|
||||||
popup.textContent = options.label;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return popup;
|
return popup;
|
||||||
|
52
src/util/paths.ts
Normal file
52
src/util/paths.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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 {Direction, LatLngExpression, PathOptions} from "leaflet";
|
||||||
|
import {LiveAtlasPath} from "@/index";
|
||||||
|
|
||||||
|
export const tooltipOptions = {
|
||||||
|
direction: 'top' as Direction,
|
||||||
|
sticky: true,
|
||||||
|
opacity: 1.0,
|
||||||
|
interactive: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const arePointsEqual = (oldPoints: LatLngExpression | LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][],
|
||||||
|
newPoints: LatLngExpression | LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][]) => {
|
||||||
|
return JSON.stringify(oldPoints) === JSON.stringify(newPoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isStyleEqual = (oldStyle: PathOptions, newStyle: PathOptions) => {
|
||||||
|
return oldStyle && newStyle
|
||||||
|
&& (oldStyle.color === newStyle.color)
|
||||||
|
&& (oldStyle.weight === newStyle.weight)
|
||||||
|
&& (oldStyle.opacity === newStyle.opacity)
|
||||||
|
&& (oldStyle.fillColor === newStyle.fillColor)
|
||||||
|
&& (oldStyle.fillOpacity === newStyle.fillOpacity)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createPopup = (options: LiveAtlasPath, className: string): HTMLElement => {
|
||||||
|
const popup = document.createElement('span');
|
||||||
|
|
||||||
|
if(options.isPopupHTML) {
|
||||||
|
popup.classList.add(className);
|
||||||
|
popup.insertAdjacentHTML('afterbegin', options.popupContent as string);
|
||||||
|
} else {
|
||||||
|
popup.textContent = options.popupContent as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
return popup;
|
||||||
|
};
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
const app = document.getElementById('app'),
|
const app = document.getElementById('app'),
|
||||||
splash = document.getElementById('splash'),
|
splash = document.getElementById('splash'),
|
||||||
splashSpinner = document.getElementById('splash__spinner'),
|
splashSpinner = document.getElementById('splash__spinner'),
|
||||||
@ -54,4 +70,4 @@ export const showSplashError = (message: string, fatal: boolean, attempts?: numb
|
|||||||
splashRetry.textContent = `Retrying... (${attempts.toString()})`;
|
splashRetry.textContent = `Retrying... (${attempts.toString()})`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user