From 7056b443d5275c18bfdb97c57db294cf5b27761a Mon Sep 17 00:00:00 2001 From: Joshua Grimm Date: Sat, 26 Oct 2019 16:20:05 +0200 Subject: [PATCH] inital commit --- Dockerfile | 9 + README.md | 63 ++++ dashboard/player-stats.json | 711 ++++++++++++++++++++++++++++++++++++ dashboard/server-stats.json | 421 +++++++++++++++++++++ minecraft_exporter.py | 163 +++++++++ 5 files changed, 1367 insertions(+) create mode 100644 Dockerfile create mode 100644 dashboard/player-stats.json create mode 100644 dashboard/server-stats.json create mode 100644 minecraft_exporter.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..cdaeb4b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +from python:3 + +RUN pip install nbt mcrcon prometheus_client requests + +COPY minecraft_exporter.py / + +EXPOSE 8001 + +ENTRYPOINT ["python","minecraft_exporter.py"] diff --git a/README.md b/README.md index 68f4b83..9e287d8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,65 @@ # minecraft-exporter + this is a prometheus minecraft exporter +This exporter reads minecrafts nbt files, the advancements files and can optionally connect via RCON to your minecraft server. + +to use it mount your world to /world in the container + +rcon connection only works on forge servers, it only executes `forge tps` to get tps and tick time informations + +to enable rcon on your minecraft server add the following to the server.properties file: + +``` +broadcast-rcon-to-ops=false +rcon.port=25575 +rcon.password=Password +enable-rcon=true +``` + +The RCON Module is only enabled if `RCON_HOST` and `RCON_PASSWORD` is set + + +# Usage + +``` +docker run -e RCON_HOST=127.0.0.1 \ + -e RCON_PORT=25575 \ + -e RCON_PASSWORD="Password" \ + -p 8000:8000 \ + -v /opt/all_the_mods_3/world:/world \ + joshi425/minecraft_exporter +``` + +# Metrics + +``` +blocks_mined +blocks_picked_up +player_deaths +player_jumps +cm_traveled +player_xp_total +player_current_level +player_food_level +player_health +player_score +entities_killed +damage_taken +damage_dealt +blocks_crafted +player_playtime +player_advancements +player_slept +player_used_crafting_table +``` +the following Metrics are only exported if RCON is configured: +``` +dim_tps +dim_ticktime +overall_tps +overall_ticktime +``` + +# Dashboards + +In the folder dashboards you'll find grafana dashboards for these metrics, they are however incomplete and can be expanded diff --git a/dashboard/player-stats.json b/dashboard/player-stats.json new file mode 100644 index 0000000..c45da85 --- /dev/null +++ b/dashboard/player-stats.json @@ -0,0 +1,711 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "6.4.3" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "singlestat", + "name": "Singlestat", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "iteration": 1572099073069, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(blocks_mined{player=\"$player\"}) by (block)", + "legendFormat": "{{block}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Blocks mined", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 3, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(blocks_crafted{player=\"$player\"}) by (block)", + "legendFormat": "{{block}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "items crafted", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(blocks_picked_up{player=\"$player\"}) by (block)", + "legendFormat": "{{block}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Blocks collected", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "${DS_PROMETHEUS}", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 9, + "w": 5, + "x": 12, + "y": 9 + }, + "id": 5, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(player_deaths{player=\"$player\"})", + "instant": true, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, + "title": "Deaths", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "${DS_PROMETHEUS}", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 9, + "w": 5, + "x": 17, + "y": 9 + }, + "id": 6, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "player_jumps{player=\"$player\"}", + "instant": true, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, + "title": "Jumps", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 7, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "cm_traveled{player=\"$player\"} / 1000", + "legendFormat": "{{ method }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Meters travelled", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "lengthm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "entities_killed{player=\"$player\"}", + "legendFormat": "{{ entity }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Mobs killed", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": false, + "schemaVersion": 20, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "label_values(blocks_mined,player)", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "player", + "options": [], + "query": "label_values(blocks_mined,player)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "minecraft Player stats", + "uid": "gAy914AZk", + "version": 8 +} diff --git a/dashboard/server-stats.json b/dashboard/server-stats.json new file mode 100644 index 0000000..977cf21 --- /dev/null +++ b/dashboard/server-stats.json @@ -0,0 +1,421 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "6.4.3" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 7, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "dim_ticktime", + "legendFormat": "{{dimension_name}}", + "refId": "A" + }, + { + "expr": "overall_ticktime", + "legendFormat": "Overall", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Ticktime", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 5, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "dim_tps", + "legendFormat": "{{ dimension_name }}", + "refId": "A" + }, + { + "expr": "overall_tps", + "legendFormat": "Overall", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "TPS ", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "player_score", + "legendFormat": "{{ player }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Score", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 3, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "player_playtime / 20", + "legendFormat": "{{ player }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Play Time", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "schemaVersion": 20, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Minecraft Server", + "uid": "LhW0bV0Wz", + "version": 3 +} diff --git a/minecraft_exporter.py b/minecraft_exporter.py new file mode 100644 index 0000000..d357990 --- /dev/null +++ b/minecraft_exporter.py @@ -0,0 +1,163 @@ +from prometheus_client import start_http_server, REGISTRY, Metric +import time +import requests +import json +import nbt +import re +import os +from mcrcon import MCRcon +from os import listdir +from os.path import isfile, join + +class MinecraftCollector(object): + def __init__(self): + self.statsdirectory = "/world/stats" + self.playerdirectory = "/world/playerdata" + self.advancementsdirectory = "/world/advancements" + self.map = dict() + + def get_players(self): + return [f[:-5] for f in listdir(self.statsdirectory) if isfile(join(self.statsdirectory, f))] + + def uuid_to_player(self,uuid): + uuid = uuid.replace('-','') + if uuid in self.map: + return self.map[uuid] + else: + result = requests.get('https://api.mojang.com/user/profiles/'+uuid+'/names') + self.map[uuid] = result.json()[0]['name'] + return(result.json()[0]['name']) + def get_server_stats(self): + if not all(x in os.environ for x in ['RCON_HOST','RCON_PASSWORD']): + return [] + dim_tps = Metric('dim_tps','TPS of a dimension',"counter") + dim_ticktime = Metric('dim_ticktime',"Time a Tick took in a Dimension","counter") + overall_tps = Metric('overall_tps','overall TPS',"counter") + overall_ticktime = Metric('overall_ticktime',"overall Ticktime","counter") + mcr = MCRcon(os.environ['RCON_HOST'],os.environ['RCON_PASSWORD'],port=int(os.environ['RCON_PORT'])) + mcr.connect() + resp = mcr.command("forge tps") + dimtpsregex = re.compile("Dim\s*(-*\d*)\s\((.*?)\)\s:\sMean tick time:\s(.*?) ms\. Mean TPS: (\d*\.\d*)") + for dimid, dimname, meanticktime, meantps in dimtpsregex.findall(resp): + dim_tps.add_sample('dim_tps',value=meantps,labels={'dimension_id':dimid,'dimension_name':dimname}) + dim_ticktime.add_sample('dim_ticktime',value=meanticktime,labels={'dimension_id':dimid,'dimension_name':dimname}) + overallregex = re.compile("Overall : Mean tick time: (.*) ms. Mean TPS: (.*)") + overall_tps.add_sample('overall_tps',value=overallregex.findall(resp)[0][1],labels={}) + overall_ticktime.add_sample('overall_ticktime',value=overallregex.findall(resp)[0][0],labels={}) + return[dim_tps,dim_ticktime,overall_tps,overall_ticktime] + + + def get_player_stats(self,uuid): + with open(self.statsdirectory+"/"+uuid+".json") as json_file: + data = json.load(json_file) + json_file.close() + nbtfile = nbt.nbt.NBTFile(self.playerdirectory+"/"+uuid+".dat",'rb') + data["stat.XpTotal"] = nbtfile.get("XpTotal").value + data["stat.XpLevel"] = nbtfile.get("XpLevel").value + data["stat.Score"] = nbtfile.get("Score").value + data["stat.Health"] = nbtfile.get("Health").value + data["stat.foodLevel"]= nbtfile.get("foodLevel").value + with open(self.advancementsdirectory+"/"+uuid+".json") as json_file: + count = 0 + advancements = json.load(json_file) + for key, value in advancements.items(): + if value["done"] == True: + count += 1 + data["stat.advancements"] = count + return data + + def update_metrics_for_player(self,uuid): + data = self.get_player_stats(uuid) + name = self.uuid_to_player(uuid) + blocks_mined = Metric('blocks_mined','Blocks a Player mined',"counter") + blocks_picked_up = Metric('blocks_picked_up','Blocks a Player picked up',"counter") + player_deaths = Metric('player_deaths','How often a Player died',"counter") + player_jumps = Metric('player_jumps','How often a Player has jumped',"counter") + cm_traveled = Metric('cm_traveled','How many cm a Player traveled, whatever that means',"counter") + player_xp_total = Metric('player_xp_total',"How much total XP a player has","counter") + player_current_level= Metric('player_current_level',"How much current XP a player has","counter") + player_food_level = Metric('player_food_level',"How much food the player currently has","counter") + player_health = Metric('player_health',"How much Health the player currently has","counter") + player_score = Metric('player_score',"The Score of the player","counter") + entities_killed = Metric('entities_killed',"Entities killed by player","counter") + damage_taken = Metric('damage_taken',"Damage Taken by Player","counter") + damage_dealt = Metric('damage_dealt',"Damage dealt by Player","counter") + blocks_crafted = Metric('blocks_crafted',"Items a Player crafted","counter") + player_playtime = Metric('player_playtime',"Time in Minutes a Player was online","counter") + player_advancements = Metric('player_advancements', "Number of completed advances of a player","counter") + player_slept = Metric('player_slept',"Times a Player slept in a bed","counter") + player_used_crafting_table = Metric('player_used_crafting_table',"Times a Player used a Crafting Table","counter") + for key, value in data.items(): + stat = key.split(".")[1] # entityKilledBy + if stat == "mineBlock": + blocks_mined.add_sample("blocks_mined",value=value,labels={'player':name,'block':'.'.join((key.split(".")[2],key.split(".")[3]))}) + elif stat == "pickup": + blocks_picked_up.add_sample("blocks_picked_up",value=value,labels={'player':name,'block':'.'.join((key.split(".")[2],key.split(".")[3]))}) + elif stat == "entityKilledBy": + if len(key.split(".")) == 4: + player_deaths.add_sample('player_deaths',value=value,labels={'player':name,'cause':'.'.join((key.split(".")[2],key.split(".")[3]))}) + else: + player_deaths.add_sample('player_deaths',value=value,labels={'player':name,'cause':key.split(".")[2]}) + elif stat == "jump": + player_jumps.add_sample("player_jumps",value=value,labels={'player':name}) + elif stat == "walkOneCm": + cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"walking"}) + elif stat == "swimOneCm": + cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"swimming"}) + elif stat == "sprintOneCm": + cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"sprinting"}) + elif stat == "diveOneCm": + cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"diving"}) + elif stat == "fallOneCm": + cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"falling"}) + elif stat == "flyOneCm": + cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"flying"}) + elif stat == "boatOneCm": + cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"boat"}) + elif stat == "horseOneCm": + cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"horse"}) + elif stat == "climbOneCm": + cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"climbing"}) + elif stat == "XpTotal": + player_xp_total.add_sample('player_xp_total',value=value,labels={'player':name}) + elif stat == "XpLevel": + player_current_level.add_sample('player_current_level',value=value,labels={'player':name}) + elif stat == "foodLevel": + player_food_level.add_sample('player_food_level',value=value,labels={'player':name}) + elif stat == "Health": + player_health.add_sample('player_health',value=value,labels={'player':name}) + elif stat == "Score": + player_score.add_sample('player_score',value=value,labels={'player':name}) + elif stat == "killEntity": + entities_killed.add_sample('entities_killed',value=value,labels={'player':name,"entity":key.split(".")[2]}) + elif stat == "damageDealt": + damage_dealt.add_sample('damage_dealt',value=value,labels={'player':name}) + elif stat == "damageTaken": + damage_dealt.add_sample('damage_taken',value=value,labels={'player':name}) + elif stat == "craftItem": + blocks_crafted.add_sample('blocks_crafted',value=value,labels={'player':name,'block':'.'.join((key.split(".")[2],key.split(".")[3]))}) + elif stat == "playOneMinute": + player_playtime.add_sample('player_playtime',value=value,labels={'player':name}) + elif stat == "advancements": + player_advancements.add_sample('player_advancements',value=value,labels={'player':name}) + elif stat == "sleepInBed": + player_slept.add_sample('player_slept',value=value,labels={'player':name}) + elif stat == "craftingTableInteraction": + player_used_crafting_table.add_sample('player_used_crafting_table',value=value,labels={'player':name}) + return [blocks_mined,blocks_picked_up,player_deaths,player_jumps,cm_traveled,player_xp_total,player_current_level,player_food_level,player_health,player_score,entities_killed,damage_taken,damage_dealt,blocks_crafted,player_playtime,player_advancements,player_slept,player_used_crafting_table] + + def collect(self): + for player in self.get_players(): + for metric in self.update_metrics_for_player(player)+self.get_server_stats(): + yield metric + + +if __name__ == '__main__': + if all(x in os.environ for x in ['RCON_HOST','RCON_PASSWORD']): + print("RCON is enabled for "+ os.environ['RCON_HOST']) + + start_http_server(8000) + REGISTRY.register(MinecraftCollector()) + print("Exporter started on Port 8000") + while True: + time.sleep(1)