diff --git a/doc/pics/stats_webfragment.png b/doc/pics/stats_webfragment.png new file mode 100644 index 0000000..5a9eba6 Binary files /dev/null and b/doc/pics/stats_webfragment.png differ diff --git a/doc/stats_webfragment.md b/doc/stats_webfragment.md new file mode 100644 index 0000000..d6f015e --- /dev/null +++ b/doc/stats_webfragment.md @@ -0,0 +1,11 @@ + +# Stats webfragment + + + +The "world stats" info from the bottom right corner of the mapserver +can be embedded as an iframe into any existing web-page: + +```html + +``` diff --git a/readme.md b/readme.md index 35c0625..b841f9c 100644 --- a/readme.md +++ b/readme.md @@ -23,6 +23,7 @@ Demo: [Pandorabox Server map](https://pandorabox.io/map/#-1782.25/493.5/10) * [Search](doc/search.md) * [Configuration](doc/config.md) * [Recommended specs](doc/recommended_specs.md) +* [Stats webfragment](doc/stats_webfragment.md) * [Contribution](doc/contrib.md) * [Development](doc/dev.md) * [License](doc/license.md) diff --git a/static/js/bundle-stats.js b/static/js/bundle-stats.js new file mode 100644 index 0000000..dbc7ce2 --- /dev/null +++ b/static/js/bundle-stats.js @@ -0,0 +1,104 @@ +(function(f){typeof define==='function'&&define.amd?define(f):f();}((function(){'use strict';function WorldStats(info){ + + var timeIcon = m("span", { class: "fa fa-sun", style: "color: orange;" }); + + if (info.time < 5500 || info.time > 19000) //0 - 24'000 + timeIcon = m("span", { class: "fa fa-moon", style: "color: blue;" }); + + function getHour(){ + return Math.floor(info.time/1000); + } + + function getMinute(){ + var min = Math.floor((info.time % 1000) / 1000 * 60); + return min > 10 ? min : "0" + min; + } + + function getLag(){ + var color = "green"; + if (info.max_lag > 0.8) + color = "orange"; + else if (info.max_lag > 1.2) + color = "red"; + + return [ + m("span", { class: "fa fa-wifi", style: "color: " + color }), + parseInt(info.max_lag*1000), + " ms" + ]; + } + + function getPlayers(){ + return [ + m("span", { class: "fa fa-users" }), + info.players ? info.players.length : "0" + ]; + } + + return m("div", [ + getPlayers(), + " ", + getLag(), + " ", + m("span", { class: "fa fa-clock" }), + timeIcon, + getHour(), ":", getMinute() + ]); + +}class WebSocketChannel { + constructor(){ + this.wsUrl = window.location.protocol.replace("http", "ws") + + "//" + window.location.host + + window.location.pathname.substring(0, window.location.pathname.lastIndexOf("/")) + + "/api/ws"; + + this.listenerMap = {/* type -> [listeners] */}; + } + + addListener(type, listener){ + var list = this.listenerMap[type]; + if (!list){ + list = []; + this.listenerMap[type] = list; + } + + list.push(listener); + } + + removeListener(type, listener){ + var list = this.listenerMap[type]; + if (!list){ + return; + } + + this.listenerMap[type] = list.filter(l => l != listener); + } + + connect(){ + var ws = new WebSocket(this.wsUrl); + var self = this; + + ws.onmessage = function(e){ + var event = JSON.parse(e.data); + //rendered-tile, mapobject-created, mapobjects-cleared + + var listeners = self.listenerMap[event.type]; + if (listeners){ + listeners.forEach(function(listener){ + listener(event.data); + }); + } + }; + + ws.onerror = function(){ + //reconnect after some time + setTimeout(self.connect.bind(self), 1000); + }; + } +} + +var wsChannel = new WebSocketChannel();wsChannel.connect(); + +wsChannel.addListener("minetest-info", function(info){ + m.render(document.getElementById("app"), WorldStats(info)); +});})));//# sourceMappingURL=bundle-stats.js.map diff --git a/static/js/bundle-stats.js.map b/static/js/bundle-stats.js.map new file mode 100644 index 0000000..d98b2d8 --- /dev/null +++ b/static/js/bundle-stats.js.map @@ -0,0 +1 @@ +{"version":3,"file":"bundle-stats.js","sources":["components/WorldStats.js","WebSocketChannel.js","stats.js"],"sourcesContent":["\nexport default function(info){\n\n var timeIcon = m(\"span\", { class: \"fa fa-sun\", style: \"color: orange;\" });\n\n if (info.time < 5500 || info.time > 19000) //0 - 24'000\n timeIcon = m(\"span\", { class: \"fa fa-moon\", style: \"color: blue;\" });\n\n function getHour(){\n return Math.floor(info.time/1000);\n }\n\n function getMinute(){\n var min = Math.floor((info.time % 1000) / 1000 * 60);\n return min > 10 ? min : \"0\" + min;\n }\n\n function getLag(){\n var color = \"green\";\n if (info.max_lag > 0.8)\n color = \"orange\";\n else if (info.max_lag > 1.2)\n color = \"red\";\n\n return [\n m(\"span\", { class: \"fa fa-wifi\", style: \"color: \" + color }),\n parseInt(info.max_lag*1000),\n \" ms\"\n ];\n }\n\n function getPlayers(){\n return [\n m(\"span\", { class: \"fa fa-users\" }),\n info.players ? info.players.length : \"0\"\n ];\n }\n\n return m(\"div\", [\n getPlayers(),\n \" \",\n getLag(),\n \" \",\n m(\"span\", { class: \"fa fa-clock\" }),\n timeIcon,\n getHour(), \":\", getMinute()\n ]);\n\n};\n","\nclass WebSocketChannel {\n constructor(){\n this.wsUrl = window.location.protocol.replace(\"http\", \"ws\") +\n \"//\" + window.location.host +\n window.location.pathname.substring(0, window.location.pathname.lastIndexOf(\"/\")) +\n \"/api/ws\";\n\n this.listenerMap = {/* type -> [listeners] */};\n }\n\n addListener(type, listener){\n var list = this.listenerMap[type];\n if (!list){\n list = [];\n this.listenerMap[type] = list;\n }\n\n list.push(listener);\n }\n\n removeListener(type, listener){\n var list = this.listenerMap[type];\n if (!list){\n return;\n }\n\n this.listenerMap[type] = list.filter(l => l != listener);\n }\n\n connect(){\n var ws = new WebSocket(this.wsUrl);\n var self = this;\n\n ws.onmessage = function(e){\n var event = JSON.parse(e.data);\n //rendered-tile, mapobject-created, mapobjects-cleared\n\n var listeners = self.listenerMap[event.type];\n if (listeners){\n listeners.forEach(function(listener){\n listener(event.data);\n });\n }\n };\n\n ws.onerror = function(){\n //reconnect after some time\n setTimeout(self.connect.bind(self), 1000);\n };\n }\n}\n\nexport default new WebSocketChannel();\n","\nimport WorldStats from './components/WorldStats.js';\nimport wsChannel from './WebSocketChannel.js';\n\nwsChannel.connect();\n\nwsChannel.addListener(\"minetest-info\", function(info){\n\tm.render(document.getElementById(\"app\"), WorldStats(info));\n});\n"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/static/js/components/WorldStats.js b/static/js/components/WorldStats.js index 303ea79..cb139f9 100644 --- a/static/js/components/WorldStats.js +++ b/static/js/components/WorldStats.js @@ -12,7 +12,7 @@ export default function(info){ function getMinute(){ var min = Math.floor((info.time % 1000) / 1000 * 60); - return min > 10 ? min : "0" + min; + return min >= 10 ? min : "0" + min; } function getLag(){ diff --git a/static/js/rollup.config.js b/static/js/rollup.config.js index bc5416a..47ad1ea 100644 --- a/static/js/rollup.config.js +++ b/static/js/rollup.config.js @@ -1,5 +1,5 @@ -export default { +export default [{ input: 'main.js', output: { file :'bundle.js', @@ -7,4 +7,12 @@ export default { sourcemap: true, compact: true } -}; +},{ + input: 'stats.js', + output: { + file :'bundle-stats.js', + format: 'umd', + sourcemap: true, + compact: true + } +}]; diff --git a/static/js/stats.js b/static/js/stats.js index e69de29..c9643d5 100644 --- a/static/js/stats.js +++ b/static/js/stats.js @@ -0,0 +1,9 @@ + +import WorldStats from './components/WorldStats.js'; +import wsChannel from './WebSocketChannel.js'; + +wsChannel.connect(); + +wsChannel.addListener("minetest-info", function(info){ + m.render(document.getElementById("app"), WorldStats(info)); +}); diff --git a/static/stats.html b/static/stats.html index a761b20..3842994 100644 --- a/static/stats.html +++ b/static/stats.html @@ -14,9 +14,9 @@ - + - +