import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { socket: null, autoUpdate: false, coordinates: [0, 0], playersLayer: true, buildingsLayer: true, poiLayer: true, travelnetLayer: true, otherLayer: true, players: [] }, mutations: { updateCoordinates(state, value) { state.coordinates = value; }, setAutoUpdate(state, value) { state.autoUpdate = value }, updatePlayersLayer(state, value) { state.playersLayer = value; }, updateBuildingsLayer(state, value) { state.buildingsLayer = value; }, updatePoiLayer(state, value) { state.poiLayer = value; }, updateTravelnetLayer(state, value) { state.travelnetLayer = value; }, updateOtherLayer(state, value) { state.otherLayer = value; }, setPlayers(state, value) { state.players = value; }, setPlayerState(state, value) { const p = state.players.find(p => p.name === value.name) p.online = value.state; } }, actions: { stopAutoUpdate: function({ state }) { if (this.socket) { var s = state.socket; state.socket = null; s.close(); } }, manualUpdate: function({commit}) { var tiles = document.getElementsByTagName("img"); for (var i = 0; i < tiles.length; i++) { var img = tiles[i]; var cl = img.getAttribute("class"); if (cl.indexOf("leaflet-tile-loaded") >= 0) { var src = img.src; var idx = src.lastIndexOf("#"); if (idx >= 0) { src = src.substring(0, idx); } img.src = src + "#" + Math.random(); } } fetch("players").then(response => response.json().then((data) => { commit("setPlayers", data); })); }, runAutoUpdate: function({ commit, state, dispatch}) { var me = this; // TODO: Make the URL to websocket configurable via .env state.socket = new WebSocket('ws://' + window.location.host + '/socket'); state.socket.onerror = function() { commit('setAutoUpdate', false) dispatch('stopAutoUpdate'); }; state.socket.onclose = function() { commit('setAutoUpdate', false) state.socket = null; } state.socket.onopen = function() { // Sending pings every 5 secs to keep connection alive. var heartbeat = function() { if (heartbeat && me.socket) { me.socket.send("PING"); setTimeout(heartbeat, 8000); } else { // Prevent sending pings to re-opened sockets. heartbeat = null; } }; setTimeout(heartbeat, 8000); }; state.socket.onmessage = function(evt) { var json = evt.data; if (!(typeof json === "string")) { return; } var msg; try { msg = JSON.parse(json); } catch (err) { return; } if (msg.players) { commit('setPlayers', msg.players); } var tilesData = msg.tiles; if (!tilesData) { return; } var invalidate = function(td) { var pyramid = new Array(9); var last = new Object(); pyramid[8] = last; for (var i = 0; i < td.length; i++) { var xz = td[i]; last[xz.X + "#" + xz.Z] = xz; } for (var p = 7; p >= 0; p--) { var prev = pyramid[p+1]; var curr = new Object(); pyramid[p] = curr; for (var k in prev) { if (Object.prototype.hasOwnProperty.call(prev, k)) { var oxz = prev[k]; var nxz = { X: oxz.X >> 1, Z: oxz.Z >> 1 }; curr[nxz.X + "#" + nxz.Z] = nxz; } } } return function(x, y, z) { if (y > 8) { x >>= y - 8; z >>= y - 8; y = 8; } var level = pyramid[y]; var k = x + "#" + z; return Object.prototype.hasOwnProperty.call(level, k); }; } (tilesData); var tiles = document.getElementsByTagName('img'); var re = /\/map\/([0-9]+)\/([0-9]+)\/([0-9]+).*/; for (var i = 0; i < tiles.length; i++) { var img = tiles[i]; var cl = img.getAttribute('class'); if (cl.indexOf('leaflet-tile-loaded') < 0) { continue; } var src = img.src; var coord = src.match(re); if (coord == null) { continue; } var y = parseInt(coord[1]); var x = parseInt(coord[2]); var z = parseInt(coord[3]); if (invalidate(x, y, z)) { var idx = src.lastIndexOf('#'); if (idx >= 0) { src = src.substring(0, idx); } img.src = src + '#' + Math.random(); } } }; } } })