Initial work for multiple servers

This commit is contained in:
James Lyne 2021-05-17 03:39:25 +01:00
parent 9720bf90d9
commit 0facb446f5
19 changed files with 1119 additions and 2826 deletions

View File

@ -20,8 +20,49 @@
<meta name="description" content="Minecraft Dynamic Map" />
<title>Minecraft Dynamic Map - LiveAtlas</title>
<!-- Remove this if you are using multiple maps -->
<script src="./standalone/config.js"></script>
<script>
window.liveAtlasConfig = {
// Server URLS can be defined here instead of using the standalone/config.js file.
// Multiple servers are supported too.
// servers: {
// creative: {
// label: 'Creative',
// configuration: 'http://dynmap.local/standalone/creative/MySQL_configuration.php',
// update: 'http://dynmap.local/standalone/creative/MySQL_update.php?world={world}&ts={timestamp}',
// sendmessage: 'http://dynmap.local/standalone/creative/MySQL_sendmessage.php',
// login: 'http://dynmap.local/standalone/creative/MySQL_login.php',
// register: 'http://dynmap.local/standalone/creative/MySQL_register.php',
// tiles: 'http://dynmap.local/standalone/creative/MySQL_tiles.php?tile=',
// markers: 'http://dynmap.local/standalone/creative/MySQL_markers.php?marker='
// },
// survival: {
// label: 'Survival',
// configuration: 'http://dynmap.local/standalone/survival/MySQL_configuration.php',
// update: 'http://dynmap.local/standalone/survival/MySQL_update.php?world={world}&ts={timestamp}',
// sendmessage: 'http://dynmap.local/standalone/survival/MySQL_sendmessage.php',
// login: 'http://dynmap.local/standalone/survival/MySQL_login.php',
// register: 'http://dynmap.local/standalone/survival/MySQL_register.php',
// tiles: 'http://dynmap.local/standalone/survival/MySQL_tiles.php?tile=',
// markers: 'http://dynmap.local/standalone/survival/MySQL_markers.php?marker='
// },
// build: {
// label: 'Build',
// configuration: 'http://dynmap.local/standalone/build/MySQL_configuration.php',
// update: 'http://dynmap.local/standalone/build/MySQL_update.php?world={world}&ts={timestamp}',
// sendmessage: 'http://dynmap.local/standalone/build/MySQL_sendmessage.php',
// login: 'http://dynmap.local/standalone/build/MySQL_login.php',
// register: 'http://dynmap.local/standalone/build/MySQL_register.php',
// tiles: 'http://dynmap.local/standalone/build/MySQL_tiles.php?tile=',
// markers: 'http://dynmap.local/standalone/build/MySQL_markers.php?marker='
// }
// }
};
</script>
<style>
/* Theme colours */
:root {
--background-base: #222222; /* Buttons/sidebar */
--background-dark: #121212; /* Body/map labels */
@ -171,7 +212,6 @@
</noscript>
<div id="mcmap" class="dynmap"></div>
<script src="./standalone/config.js"></script>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

3370
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,32 +9,28 @@
"lint": "eslint src",
"lint:fix": "eslint src --fix"
},
"dependencies": {
"core-js": "^3.6.5",
"vue": "^3.0.0"
},
"dependencies": {},
"devDependencies": {
"@types/clipboard": "^2.0.1",
"@types/leaflet": "^1.5.19",
"@typescript-eslint/eslint-plugin": "^4.1.0",
"@typescript-eslint/parser": "^4.1.0",
"@types/leaflet": "^1.7.0",
"@typescript-eslint/eslint-plugin": "^4.23.0",
"@typescript-eslint/parser": "^4.23.0",
"@vitejs/plugin-vue": "^1.2.2",
"@vue/compiler-sfc": "^3.0.0",
"@vue/eslint-config-typescript": "^5.0.2",
"clipboard": "^2.0.6",
"eslint": "^7.5.0",
"eslint-plugin-vue": "^7.0.0-0",
"@vue/compiler-sfc": "^3.0.11",
"@vue/eslint-config-typescript": "^7.0.0",
"clipboard": "^2.0.8",
"eslint": "^7.26.0",
"eslint-plugin-vue": "^7.9.0",
"leaflet": "^1.7.1",
"normalize-scss": "^7.0.1",
"sass": "^1.29.0",
"svgo": "^1.1.0",
"svgo-loader": "^2.1.0",
"typescript": "~3.9.3",
"sass": "^1.32.13",
"typescript": "~4.2.4",
"vite": "^2.3.2",
"vite-plugin-cdn": "^1.0.0-beta.3",
"vite-plugin-svg-sprite-component": "^1.0.8",
"vue-cli-plugin-svg-sprite": "~1.0.0",
"vue": "^3.0.11",
"vue-tsc": "^0.1.2",
"vuex": "^4.0.0-rc.1"
"vuex": "^4.0.0"
},
"eslintConfig": {
"root": true,

View File

@ -44,6 +44,7 @@ export default defineComponent({
updateInterval = computed(() => store.state.configuration.updateInterval),
title = computed(() => store.state.configuration.title),
currentUrl = computed(() => store.getters.url),
currentServer = computed(() => store.state.currentServer),
chatBoxEnabled = computed(() => store.state.components.chatBox),
chatVisible = computed(() => store.state.ui.visibleElements.has('chat')),
updatesEnabled = ref(false),
@ -51,24 +52,14 @@ export default defineComponent({
configAttempts = ref(0),
loadConfiguration = () => {
let validated = false;
API.validateConfiguration().then(() => {
validated = true;
return store.dispatch(ActionTypes.LOAD_CONFIGURATION, undefined).then(() => {
startUpdates();
window.hideSplash();
});
}).catch(e => {
console.error('Failed to load server configuration: ', e);
window.showSplashError(e, !validated, ++configAttempts.value);
//Don't retry if config isn't valid
if(validated) {
window.showSplashError(e, false, ++configAttempts.value);
setTimeout(() => loadConfiguration(), 1000);
}
})
});
},
startUpdates = () => {
@ -121,6 +112,15 @@ export default defineComponent({
watch(title, (title) => document.title = title);
watch(currentUrl, (url) => window.history.replaceState({}, '', url));
watch(currentServer, (newServer) => {
window.showSplash();
stopUpdates();
window.history.replaceState({}, '', newServer);
store.commit(MutationTypes.CLEAR_PARSED_URL, undefined);
store.commit(MutationTypes.CLEAR_CURRENT_ZOOM, undefined);
loadConfiguration();
});
onMounted(() => loadConfiguration());
onBeforeUnmount(() => stopUpdates());

View File

@ -36,6 +36,7 @@ import {
} from "@/dynmap";
import {useStore} from "@/store";
import ChatError from "@/errors/ChatError";
import {LiveAtlasServerDefinition} from "@/index";
function buildServerConfig(response: any): DynmapServerConfig {
return {
@ -553,38 +554,98 @@ function buildUpdates(data: Array<any>): DynmapUpdates {
}
export default {
validateConfiguration(): Promise<void> {
validateConfiguration(): Promise<Map<string, LiveAtlasServerDefinition>> {
if (typeof window.liveAtlasConfig.servers !== 'undefined') {
return this.validateLiveAtlasConfiguration(window.liveAtlasConfig.servers);
}
return this.validateDynmapConfiguration(window.config.url ?? null);
},
validateLiveAtlasConfiguration(config: any): Promise<Map<string, LiveAtlasServerDefinition>> {
const check = '\nCheck your LiveAtlas configuration in index.html is correct.',
result = new Map<string, LiveAtlasServerDefinition>();
if (!Object.keys(config).length) {
return Promise.reject(`No servers defined in LiveAtlas configuration.`);
}
for (const server in config) {
if (!Object.hasOwnProperty.call(config, server)) {
continue;
}
const serverConfig = config[server];
if (!serverConfig || serverConfig.constructor !== Object) {
return Promise.reject(`Server '${server} has an invalid configuration. ${check}`);
}
if (!serverConfig.configuration) {
return Promise.reject(`Server '${server} has no configuration URL. ${check}`);
}
if (!serverConfig.update) {
return Promise.reject(`Server '${server} has no update URL. ${check}`);
}
if (!serverConfig.markers) {
return Promise.reject(`Server '${server} has no markers URL. ${check}`);
}
if (!serverConfig.tiles) {
return Promise.reject(`Server '${server} has no tiles URL. ${check}`);
}
if (!serverConfig.sendmessage) {
return Promise.reject(`Server '${server} has no sendmessage URL. ${check}`);
}
serverConfig.id = server;
result.set(server, serverConfig);
}
return Promise.resolve(result);
},
validateDynmapConfiguration(config: LiveAtlasServerDefinition): Promise<Map<string, LiveAtlasServerDefinition>> {
const check = '\nCheck your standalone/config.js file exists and is being loaded correctly.';
if(!window.config || !window.config.url) {
return Promise.reject(`Dynmap's configuration is missing. ${check}`);
if (!config) {
return Promise.reject(`Dynmap configuration is missing. ${check}`);
}
if(!window.config.url.configuration) {
return Promise.reject(`Dynmap's configuration URL is missing. ${check}`);
if (!config.configuration) {
return Promise.reject(`Dynmap configuration URL is missing. ${check}`);
}
if(!window.config.url.update) {
return Promise.reject(`Dynmap's update URL is missing. ${check}`);
if (!config.update) {
return Promise.reject(`Dynmap update URL is missing. ${check}`);
}
if(!window.config.url.markers) {
return Promise.reject(`Dynmap's markers URL is missing. ${check}`);
if (!config.markers) {
return Promise.reject(`Dynmap markers URL is missing. ${check}`);
}
if(!window.config.url.tiles) {
return Promise.reject(`Dynmap's tiles URL is missing. ${check}`);
if (!config.tiles) {
return Promise.reject(`Dynmap tiles URL is missing. ${check}`);
}
if(!window.config.url.sendmessage) {
return Promise.reject(`Dynmap's sendmessage URL is missing. ${check}`);
if (!config.sendmessage) {
return Promise.reject(`Dynmap sendmessage URL is missing. ${check}`);
}
return Promise.resolve();
config.id = 'dynmap';
config.label = 'Dynmap';
const result = new Map<string, LiveAtlasServerDefinition>();
result.set('dynmap', config);
return Promise.resolve(result);
},
getConfiguration(): Promise<DynmapConfigurationResponse> {
return fetch(window.config.url.configuration).then(response => {
return fetch(useStore().getters.serverConfig.configuration).then(response => {
if (!response.ok) {
throw new Error('Network request failed: ' + response.statusText);
}
@ -608,7 +669,7 @@ export default {
},
getUpdate(requestId: number, world: string, timestamp: number): Promise<DynmapUpdateResponse> {
let url = window.config.url.update;
let url = useStore().getters.serverConfig.update;
url = url.replace('{world}', world);
url = url.replace('{timestamp}', timestamp.toString());
@ -674,7 +735,7 @@ export default {
},
getMarkerSets(world: string): Promise<Map<string, DynmapMarkerSet>> {
const url = `${window.config.url.markers}_markers_/marker_${world}.json`;
const url = `${useStore().getters.serverConfig.markers}_markers_/marker_${world}.json`;
return fetch(url).then(response => {
if (!response.ok) {
@ -718,7 +779,7 @@ export default {
return Promise.reject(new ChatError("Chat is not enabled"));
}
return fetch(window.config.url.sendmessage, {
return fetch(useStore().getters.serverConfig.sendmessage, {
method: 'POST',
body: JSON.stringify({
name: null,

View File

@ -168,6 +168,11 @@ export default defineComponent({
min-height: 0;
overflow: auto;
will-change: transform;
margin-bottom: 1rem;
&:last-child {
margin-bottom: 0;
}
}
.section__skeleton {

View File

@ -0,0 +1,68 @@
<!--
- 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.
-->
<template>
<li :class="{'server': true, 'server--selected': server.id === currentServer.id}">
<button type="button" :title="server.label || server.id" @click="setCurrentServer(server.id)">{{ server.label || server.id }}</button>
</li>
</template>
<script lang="ts">
import {useStore} from "@/store";
import {defineComponent} from 'vue';
import {MutationTypes} from "@/store/mutation-types";
import {LiveAtlasServerDefinition} from "@/index";
export default defineComponent({
name: 'ServerListItem',
props: {
server: {
type: Object as () => LiveAtlasServerDefinition,
required: true
}
},
computed: {
currentServer(): LiveAtlasServerDefinition | undefined {
return useStore().state.currentServer;
}
},
methods: {
setCurrentServer(serverId: string) {
useStore().commit(MutationTypes.SET_CURRENT_SERVER, serverId);
}
}
});
</script>
<style lang="scss" scoped>
.server {
height: 3.2rem;
button {
text-align: left;
display: block;
height: 100%;
width: 100%;
border-radius: 0.5rem;
}
&.server--selected button {
background-color: var(--background-hover);
}
}
</style>

View File

@ -15,6 +15,12 @@
-->
<template>
<section class="sidebar__section" v-if="servers.size > 1">
<span class="section__heading">Servers</span>
<ul class="section__content">
<ServerListItem :server="server" v-for="[name, server] in servers" :key="name"></ServerListItem>
</ul>
</section>
<section class="sidebar__section">
<span class="section__heading">{{ heading }}</span>
<ul class="section__content">
@ -28,13 +34,15 @@
<script lang="ts">
import WorldListItem from './WorldListItem.vue';
import ServerListItem from './ServerListItem.vue';
import {defineComponent} from 'vue';
import {useStore} from "@/store";
export default defineComponent({
name: 'WorldList',
components: {
WorldListItem
WorldListItem,
ServerListItem
},
computed: {
@ -44,6 +52,10 @@ export default defineComponent({
worlds() {
return useStore().state.worlds;
},
servers() {
return useStore().state.servers;
}
},
});

8
src/dynmap.d.ts vendored
View File

@ -21,16 +21,14 @@ import {ClockControlOptions} from "@/leaflet/control/ClockControl";
declare global {
interface Window {
config: DynmapConfig;
config: { url: DynmapUrlConfig };
liveAtlasConfig: any,
hideSplash: Function;
showSplash: Function;
showSplashError: Function;
}
}
type DynmapConfig = {
url: DynmapUrlConfig;
};
type DynmapUrlConfig = {
configuration: string;
update: string;

6
src/index.d.ts vendored
View File

@ -1,5 +1,6 @@
import { ComponentCustomProperties } from 'vue'
import {State, Store} from "@/store";
import {DynmapUrlConfig} from "@/dynmap";
declare module "*.png" {
const value: any;
@ -18,3 +19,8 @@ declare module '@vue/runtime-core' {
$store: Store<State>
}
}
interface LiveAtlasServerDefinition extends DynmapUrlConfig {
id: string
label?: string
}

View File

@ -18,6 +18,7 @@
*/
import {DivIconOptions, PointExpression, Icon, DivIcon, DomUtil, point} from 'leaflet';
import {useStore} from "@/store";
export interface DynmapIconOptions extends DivIconOptions {
icon: string;
@ -59,7 +60,7 @@ export class DynmapIcon extends DivIcon {
}
const div = markerContainer.cloneNode(false) as HTMLDivElement,
url = `${window.config.url.markers}_markers_/${this.options.icon}.png`,
url = `${useStore().getters.serverConfig.markers}_markers_/${this.options.icon}.png`,
size = point(this.options.iconSize as PointExpression);
this._image = markerIcon.cloneNode(false) as HTMLImageElement;
@ -95,7 +96,7 @@ export class DynmapIcon extends DivIcon {
update(options: DynmapIconOptions) {
if(this._image && options.icon !== this.options.icon) {
this._image!.src = `${window.config.url.markers}_markers_/${options.icon}.png`;
this._image!.src = `${useStore().getters.serverConfig.markers}_markers_/${options.icon}.png`;
this.options.icon = options.icon;
}

View File

@ -20,6 +20,7 @@
import {TileLayer, Coords, DoneCallback, TileLayerOptions, DomUtil, Util, LatLng} from 'leaflet';
import {DynmapProjection} from "@/leaflet/projection/DynmapProjection";
import {Coordinate, DynmapWorldMap} from "@/dynmap";
import {store} from "@/store";
export interface DynmapTileLayerOptions extends TileLayerOptions {
mapSettings: DynmapWorldMap;
@ -111,7 +112,7 @@ export class DynmapTileLayer extends TileLayer {
if (!url) {
const path = escape(`${this._mapSettings.world.name}/${name}`);
url = `${window.config.url.tiles}${path}`;
url = `${store.getters.serverConfig.tiles}${path}`;
if(typeof timestamp !== 'undefined') {
url += (url.indexOf('?') === -1 ? `?timestamp=${timestamp}` : `&timestamp=${timestamp}`);

View File

@ -16,11 +16,13 @@
import { createApp } from 'vue'
import App from './App.vue'
import API from './api';
import {store} from "@/store";
import 'leaflet/dist/leaflet.css';
import 'normalize-scss/sass/normalize/_import-now.scss';
import '@/scss/style.scss';
import {MutationTypes} from "@/store/mutation-types";
const splash = document.getElementById('splash'),
splashSpinner = document.getElementById('splash__spinner'),
@ -30,14 +32,37 @@ const splash = document.getElementById('splash'),
splashAttempt = document.getElementById('splash__error-attempt'),
svgs = import.meta.globEager('/assets/icons/*.svg');
window.hideSplash = function() {
window.showSplash = function() {
if(!splash) {
return;
}
splash.ontransitionend = null;
splash.hidden = false;
requestAnimationFrame(function() {
if(splash) {
splash.style.opacity = '0';
splash.style.opacity = '1';
}
});
};
window.hideSplash = function() {
if(!splash) {
return;
}
splash.ontransitionend = function(e) {
if(e.target === splash) {
splash.hidden = true;
}
};
requestAnimationFrame(function() {
splash.style.opacity = '0';
});
};
window.showSplashError = function(message: string, fatal: boolean, attempts: number) {
if(splashError) {
splashError.setAttribute('aria-hidden', 'false');
@ -61,17 +86,24 @@ window.showSplashError = function(message: string, fatal: boolean, attempts: num
}
};
if(splash) {
splash.addEventListener('transitionend', function(e) {
if(e.target === splash) {
splash.hidden = true;
}
});
console.info(`LiveAtlas version ${store.state.version} - https://github.com/JLyne/LiveAtlas`);
API.validateConfiguration().then((config) => {
store.commit(MutationTypes.SET_SERVERS, config);
if(config.size > 1) {
const lastSegment = window.location.pathname.split('/').pop(),
serverName = lastSegment && config.has(lastSegment) ? lastSegment : config.keys().next().value;
store.commit(MutationTypes.SET_CURRENT_SERVER, serverName);
} else {
store.commit(MutationTypes.SET_CURRENT_SERVER, config.keys().next().value);
}
console.info(`LiveAtlas version ${store.state.version} - https://github.com/JLyne/LiveAtlas`);
console.log(store.state.currentServer);
const app = createApp(App).use(store);
// app.config.performance = true;
app.mount('#mcmap');
});

View File

@ -78,7 +78,11 @@ export interface Actions {
export const actions: ActionTree<State, State> & Actions = {
[ActionTypes.LOAD_CONFIGURATION]({commit, state}): Promise<DynmapConfigurationResponse> {
commit(MutationTypes.CLEAR_MARKER_SETS, undefined);
commit(MutationTypes.CLEAR_PLAYERS, undefined);
return API.getConfiguration().then(config => {
commit(MutationTypes.CLEAR_WORLDS, undefined);
commit(MutationTypes.SET_CONFIGURATION, config.config);
commit(MutationTypes.SET_MESSAGES, config.messages);
commit(MutationTypes.SET_WORLDS, config.worlds);

View File

@ -17,6 +17,7 @@
import {GetterTree} from "vuex";
import {State} from "@/store/state";
import {getMinecraftTime} from "@/util";
import {LiveAtlasServerDefinition} from "@/index";
export type Getters = {
playerMarkersEnabled(state: State): boolean;
@ -25,6 +26,7 @@ export type Getters = {
night(state: State): boolean;
mapBackground(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): LiveAtlasServerDefinition;
}
export const getters: GetterTree<State, State> & Getters = {
@ -72,5 +74,13 @@ export const getters: GetterTree<State, State> & Getters = {
}
return `#${state.currentWorld.name};${state.currentMap.name};${locationString};${zoom}`;
},
serverConfig(state: State): LiveAtlasServerDefinition {
if(!state.currentServer) {
throw RangeError("No current server");
}
return state.servers.get(state.currentServer) as LiveAtlasServerDefinition;
}
}

View File

@ -15,12 +15,15 @@
*/
export enum MutationTypes {
SET_SERVERS ='setServers',
SET_CONFIGURATION = 'setConfiguration',
SET_MESSAGES = 'setMessages',
SET_WORLDS = 'setWorlds',
SET_COMPONENTS = 'setComponents',
SET_MARKER_SETS = 'setMarkerSets',
CLEAR_MARKER_SETS = 'clearMarkerSets',
ADD_WORLD = 'addWorld',
CLEAR_WORLDS = 'clearWorlds',
SET_WORLD_STATE = 'setWorldState',
SET_UPDATE_TIMESTAMP = 'setUpdateTimestamp',
ADD_MARKER_SET_UPDATES = 'addMarkerSetUpdates',
@ -32,15 +35,18 @@ export enum MutationTypes {
POP_LINE_UPDATES = 'popLineUpdates',
POP_TILE_UPDATES = 'popTileUpdates',
INCREMENT_REQUEST_ID = 'incrementRequestId',
SET_PLAYERS = 'setPlayers',
SET_PLAYERS_ASYNC = 'setPlayersAsync',
CLEAR_PLAYERS = 'clearPlayers',
SYNC_PLAYERS = 'syncPlayers',
SET_CURRENT_SERVER = 'setCurrentServer',
SET_CURRENT_MAP = 'setCurrentMap',
SET_CURRENT_PROJECTION = 'setCurrentProjection',
SET_CURRENT_LOCATION = 'setCurrentLocation',
SET_CURRENT_ZOOM = 'setCurrentZoom',
CLEAR_CURRENT_ZOOM = 'clearCurrentZoom',
SET_PARSED_URL = 'setParsedUrl',
CLEAR_PARSED_URL = 'clearParsedUrl',
SET_FOLLOW_TARGET = 'setFollowTarget',
SET_PAN_TARGET = 'setPanTarget',

View File

@ -32,6 +32,7 @@ import {
DynmapWorldState, DynmapParsedUrl, DynmapChat, DynmapUIElement
} from "@/dynmap";
import {DynmapProjection} from "@/leaflet/projection/DynmapProjection";
import {LiveAtlasServerDefinition} from "@/index";
export type CurrentMapPayload = {
worldName: string;
@ -39,12 +40,15 @@ export type CurrentMapPayload = {
}
export type Mutations<S = State> = {
[MutationTypes.SET_SERVERS](state: S, servers: Map<string, LiveAtlasServerDefinition>): void
[MutationTypes.SET_CONFIGURATION](state: S, config: DynmapServerConfig): void
[MutationTypes.SET_MESSAGES](state: S, messages: DynmapMessageConfig): void
[MutationTypes.SET_WORLDS](state: S, worlds: Array<DynmapWorld>): void
[MutationTypes.SET_COMPONENTS](state: S, worlds: DynmapComponentConfig): void
[MutationTypes.SET_MARKER_SETS](state: S, worlds: Map<string, DynmapMarkerSet>): void
[MutationTypes.CLEAR_MARKER_SETS](state: S): void
[MutationTypes.ADD_WORLD](state: S, world: DynmapWorld): void
[MutationTypes.CLEAR_WORLDS](state: S): void
[MutationTypes.SET_WORLD_STATE](state: S, worldState: DynmapWorldState): void
[MutationTypes.SET_UPDATE_TIMESTAMP](state: S, time: Date): void
[MutationTypes.ADD_MARKER_SET_UPDATES](state: S, updates: Map<string, DynmapMarkerSetUpdates>): void
@ -60,11 +64,15 @@ export type Mutations<S = State> = {
[MutationTypes.INCREMENT_REQUEST_ID](state: S): void
[MutationTypes.SET_PLAYERS_ASYNC](state: S, players: Set<DynmapPlayer>): Set<DynmapPlayer>
[MutationTypes.SYNC_PLAYERS](state: S, keep: Set<string>): void
[MutationTypes.CLEAR_PLAYERS](state: S): void
[MutationTypes.SET_CURRENT_SERVER](state: S, server: string): void
[MutationTypes.SET_CURRENT_MAP](state: S, payload: CurrentMapPayload): void
[MutationTypes.SET_CURRENT_PROJECTION](state: S, payload: DynmapProjection): void
[MutationTypes.SET_CURRENT_LOCATION](state: S, payload: Coordinate): void
[MutationTypes.SET_CURRENT_ZOOM](state: S, payload: number): void
[MutationTypes.CLEAR_CURRENT_ZOOM](state: S): void
[MutationTypes.SET_PARSED_URL](state: S, payload: DynmapParsedUrl): void
[MutationTypes.CLEAR_PARSED_URL](state: S): void
[MutationTypes.SET_FOLLOW_TARGET](state: S, payload: DynmapPlayer): void
[MutationTypes.SET_PAN_TARGET](state: S, payload: DynmapPlayer): void
[MutationTypes.CLEAR_FOLLOW_TARGET](state: S, a?: void): void
@ -78,6 +86,15 @@ export type Mutations<S = State> = {
}
export const mutations: MutationTree<State> & Mutations = {
// Sets configuration options from the initial config fetch
[MutationTypes.SET_SERVERS](state: State, config: Map<string, LiveAtlasServerDefinition>) {
state.servers = config;
if(state.currentServer && !state.servers.has(state.currentServer)) {
state.currentServer = undefined;
}
},
// Sets configuration options from the initial config fetch
[MutationTypes.SET_CONFIGURATION](state: State, config: DynmapServerConfig) {
state.configuration = Object.assign(state.configuration, config);
@ -128,10 +145,31 @@ export const mutations: MutationTree<State> & Mutations = {
}
},
//Sets the existing marker sets from the last marker fetch
[MutationTypes.CLEAR_MARKER_SETS](state: State) {
state.markerSets.clear();
state.pendingSetUpdates.clear();
},
[MutationTypes.ADD_WORLD](state: State, world: DynmapWorld) {
state.worlds.set(world.name, world);
},
[MutationTypes.CLEAR_WORLDS](state: State) {
console.log('??????');
state.worlds.clear();
state.maps.clear();
state.currentMap = undefined;
state.currentWorld = undefined;
state.followTarget = undefined;
state.panTarget = undefined;
state.currentWorldState.timeOfDay = 0;
state.currentWorldState.raining = false;
state.currentWorldState.thundering = false;
},
//Sets the current world state an update fetch
[MutationTypes.SET_WORLD_STATE](state: State, worldState: DynmapWorldState) {
state.currentWorldState = Object.assign(state.currentWorldState, worldState);
@ -341,6 +379,25 @@ export const mutations: MutationTree<State> & Mutations = {
}
},
//Removes all players not found in the provided keep set
[MutationTypes.CLEAR_PLAYERS](state: State) {
state.followTarget = undefined;
state.panTarget = undefined;
for(const [key, player] of state.players) {
state.players.delete(key);
}
},
//Sets the currently active server
[MutationTypes.SET_CURRENT_SERVER](state: State, serverName) {
if(!state.servers.has(serverName)) {
throw new RangeError(`Unknown server ${serverName}`);
}
state.currentServer = serverName;
},
//Sets the currently active map/world
[MutationTypes.SET_CURRENT_MAP](state: State, {worldName, mapName}) {
mapName = [worldName, mapName].join('_');
@ -380,11 +437,26 @@ export const mutations: MutationTree<State> & Mutations = {
state.currentZoom = payload;
},
[MutationTypes.CLEAR_CURRENT_ZOOM](state: State) {
state.currentZoom = 0;
},
//Sets the result of parsing the current map url, if present and valid
[MutationTypes.SET_PARSED_URL](state: State, payload: DynmapParsedUrl) {
state.parsedUrl = payload;
},
//Clear any existing parsed url
[MutationTypes.CLEAR_PARSED_URL](state: State) {
state.parsedUrl = {
world: undefined,
map: undefined,
location: undefined,
zoom: undefined,
legacy: false,
};
},
//Set the follow target, which the map will automatically pan to keep in view
[MutationTypes.SET_FOLLOW_TARGET](state: State, player: DynmapPlayer) {
state.followTarget = player;

View File

@ -23,9 +23,11 @@ import {
DynmapWorld, DynmapWorldState, Coordinate, DynmapParsedUrl, DynmapChat, DynmapUIElement
} from "@/dynmap";
import {DynmapProjection} from "@/leaflet/projection/DynmapProjection";
import {LiveAtlasServerDefinition} from "@/index";
export type State = {
version: string;
servers: Map<string, LiveAtlasServerDefinition>;
configuration: DynmapServerConfig;
messages: DynmapMessageConfig;
components: DynmapComponentConfig;
@ -48,6 +50,7 @@ export type State = {
followTarget?: DynmapPlayer;
panTarget?: DynmapPlayer;
currentServer?: string;
currentWorldState: DynmapWorldState;
currentWorld?: DynmapWorld;
currentMap?: DynmapWorldMap;
@ -69,6 +72,7 @@ export type State = {
export const state: State = {
version: (import.meta.env.VITE_VERSION || 'Unknown') as string,
servers: new Map(),
configuration: {
version: '',
@ -150,6 +154,7 @@ export const state: State = {
followTarget: undefined,
panTarget: undefined,
currentServer: undefined,
currentWorld: undefined,
currentMap: undefined,
currentLocation: {

View File

@ -99,7 +99,7 @@ const tickHeadQueue = () => {
src = (head.size === 'body') ? `faces/body/${head.account}.png` :`faces/${head.size}x${head.size}/${head.account}.png`;
headsLoading.add(head.cacheKey);
head.image.src = concatURL(window.config.url.markers, src);
head.image.src = concatURL(useStore().getters.serverConfig.markers, src);
tickHeadQueue();
}