Auf der Suche nach dem unbekannten BLOB

Sumpfhytte

Serverteam
Administrator
11 Aug 2014
7.790
In diesem Thema geht es vordergründig um das Sammeln von Deko-Köpfen, aber nebenbei erfahrt ihr auch, was eine "Datenbank" ist und wie sie funktioniert.

Nachdem 2014 der erste Deko-Kopf als Halloween-Eventgewinn vergeben wurde, folgten 2015 sechs weitere Köpfe zu Ostern, Halloween und Weihnachten. Und weil Deko-Köpfe so begehrt waren, bildeten sie 2016 auch die Grundlage für unseren neuen Vote-Shop. In diesem Jahr erschienen 70 neue Köpfe. Bald entstanden erste Kopfmuseen und im Forum einige Listen.
Vier Jahre später wünschte sich ein Spieler eine offizielle Übersicht im UW-Wiki. Also besuchte ich im Mai 2020 einige Kopfmuseen und nahm die Listen im Forum zur Grundlage für eine Übersicht im UW-Wiki. Zehn Tage hat diese Zusammenstellung gedauert. Dann war das Ziel, alle Deko-Köpfe vollständig aufzulisten, erreicht. Ihr findet den Artikel unter /help kopf.

Das "Problem"​

Eine interessante Zusatzinformationen in dieser Liste ist bei den Eventgewinnen natürlich die Anzahl. Meistens hatten wir die Zahlen im Forum veröffentlicht, aber nicht immer. Die Frage war nun, wie ich an die nicht veröffentlichten Zahlen von vergangenen Events komme. Mir fehlten 27 Zahlen von Events zwischen 2016 und 2019.

Leider war das Herausfinden dieser Zahlen schwierig bis unmöglich. Zwar haben wir für unsere Suchevents ein Sammel-Plugin, das jeden Sammelfund (Ei oder Kürbis) mit Spielername und Zeitstempel in die Datenbank einträgt. Dann müssen wir nur noch auswerten, welche Spieler die vorgegebenen Limits erreicht haben und die entsprechenden Gewinne verteilen. Aber bei jedem neuen Suchevent wird die Tabelle wieder geleert und neu gefüllt. Die Daten von früheren Events sind somit weg. Die einzige Möglichkeit, um an die gelöschten Daten zu kommen, wäre das Heraussuchen und Einspielen von vielen alten Datenbank-Backups gewesen. Aber das ist sehr aufwendig, dafür war das Problem nicht wichtig genug.

Was ist eigentlich eine "Datenbank"?​

Eine Datenbank ist eine besondere Form der Datensammlung. Sammeln kann man Daten auf viele Arten, z. B. in Textdateien oder in Excel-Tabellen, aber bei großen Mengen wird das unübersichtlich und das Suchen wird kompliziert. Eine Datenbank ist ein spezielles Programm zum Verwalten von großen Datenmengen mit effizienter Suchmöglichkeit.

Die Daten werden in sogenannten Tabellen abgelegt. Eine Tabelle besteht aus Zeilen und Spalten:
  • Jede Zeile ist ein Datensatz. Anfangs ist eine Tabelle leer, d. h. sie hat keine Zeilen. Sobald sie mit Daten gefüllt wird, entstehen die Zeilen.
  • Jede Spalte steht für ein einzelnes Attribut des Datensatzes. Das Attribut hat einen Namen und einen bestimmten Datentyp. Häufig verwendete Datentypen sind "Zeichenkette" für Texte, "Ganzzahl" für Zahlen ohne Nachkommastellen und "Zeitstempel" für Datum mit Uhrzeit.
  • Mathematisch gesehen stellt eine Tabelle eine algebraische Relation dar, weshalb man auch von einem Relationalen Datenmodell spricht.
Hier ein Beispiel für eine einfache Tabelle "Spieler" mit fünf Attributen (Spalten) und drei Datensätzen (Zeilen):

UUIDNameerstesLoginRangRangbeschreibung
4711-abcSumpfhytte2014-08-09 15:00AdminVerwaltet den Server
0815-xyzxXEusebiaXx2023-01-10 18:12GastBesucher ohne Freischaltung
9876-opqCoolerTyp2019-07-01 09:15SpielerFreigeschalteter Spieler

Die UUID ist eine weltweit eindeutige ID vom Datentyp "Zeichenkette", die jedem Spieler von Mojang vergeben wird (universally unique identifier).

Üblicherweise definiert man beim Anlegen einer Tabelle die erste Spalte als Schlüssel (key). Diese Spalte darf nur eindeutige Werte enthalten, die in der ganzen Tabelle nur einmal vorkommen und sich nie ändern. Der Spielername wäre auch ein eindeutiger Wert, kann sich aber ändern. Daher ist die UUID der Schlüssel für die Spielertabelle. Mit dem Schlüssel kann man gezielt auf einen ganz bestimmten Datensatz zugreifen. Beispiel: Gib mir die Spielerdaten für die UUID "4711-abc".

In unserem Beispiel haben wir aber noch ein Problem: Bei zehntausenden Spielern würde zehntausendfach dieselbe "Rangbeschreibung" in der Tabelle stehen und sie unnötig aufblähen. Um so etwas zu vermeiden, muss man sich beim Einrichten einer Datenbank die Struktur der Tabellen gut überlegen. Die Mathematik hilft dabei, denn es gibt mathematische Regeln für die sogenannte Normalisierung von Relationen, die man automatisiert anwenden kann. In unserem Beispiel würde man für den Rang eine eigene Tabelle anlegen:

IDNameBeschreibung
0GastBesucher ohne Freischaltung
1SpielerFreigeschalteter Spieler
2TrustySchaltet Gäste frei
3GuardModeriert den Chat
4BuddyHilft Spielern und dem Serverteam
5ModeratorBetreut Spieler und die Welt
6AdministratorVerwaltet den Server
7DeveloperProgrammiert die Serversoftware
8OwnerBesitzer des Servers
9SeniorEhemaliges Serverteammitglied

In der Tabelle "Spieler" wird dann nur noch der Schlüssel aus der Rangtabelle benutzt. Das Attribut "Rang-ID" ist in diesem Fall ein Fremdschlüssel auf die Tabelle "Rang".

UUIDNameerstesLoginRang-ID
4711-abcSumpfhytte2014-08-09 15:006
0815-xyzxXHeinzXx2023-01-10 18:120
9876-opqCoolerTyp2019-07-01 09:151

Der Vorteil der Normalisierung eines Datenmodells besteht nicht nur in der Verringerung der gespeicherten Datenmenge, sondern auch in der Datenkonsistenz. Vor der Normalisierung wäre es in der Spielertabelle möglich gewesen, die Rangbeschreibung bei einzelnen Spielern anders zu schreiben, z. B. bei einigen Gästen "Das ist ein Besucher ohne eine Freischaltung". So könnten nach einiger Zeit eine Vielzahl von unterschiedlichen Beschreibungen für denselben Rang entstehen. In einem normalisierten Datenmodell kann das nicht mehr passieren, weil jede Rangbeschreibung nur noch genau einmal existiert.

Zum Anlegen der Tabellen und für das Auswerten von Daten hat ein Datenbankprogramm meist eine Benutzeroberfläche mit anklickbaren Funktionen für die Datensuche. Man kann aber auch Befehle verwenden, die man SQL nennt (structured query language). Beispiel:
SELECT Name, erstesLogin FROM Spieler WHERE UUID="4711-abc"

Das Füllen einer Datenbank geschieht aber nicht über die Benutzeroberfläche. Niemand tippt zehntausende Spielerdaten ein. Das Füllen einer Datenbank geschieht über Anwendungsprogramme, die mit der Datenbank selbst nichts zu tun haben. Diese Programme verbinden sich mit der Datenbank und benutzen sie, um mit SQL-Befehlen Daten zu speichern und darauf zuzugreifen. Beispiel: Wenn ein Spieler auf der Serverzone einen Block platzieren will, könnte unser Zonen-Plugin mit der UUID des Spielers seinen Rang aus der Datenbank holen und prüfen, ob dieser Rang die place-Berechtigung in der Spawnzone hat.
Natürlich ist das im Detail alles umfangreicher und komplexer. Es beginnt schon damit, dass wir nicht nur eine, sondern sogar zwei Datenbanken haben: Unsere Hauptdatenbank hat aktuell 202 Tabellen und hält 8,3 GB Daten, die Log-Datenbank (Protokollierung von Blockabbau, Truhenöffnung etc.) hat 312 Tabellen und hält 259,5 GB Daten.

Zusammenfassend kann man sagen, dass Anwendungsprogramme (engl. applications bzw. in unserem Fall Server-Plugins) und Datenbanken zusammenwirken: Die Plugins steuern durch ihren Programmcode (if then else) die Abläufe auf dem Server, die Datenbanken speichert in ihren Tabellen die Informationen, mit denen die Plugins arbeiten. Daten und ihre Verarbeitung - das ist EDV.

Wie ging es mit den Eventzahlen weiter?​

Nachdem uns das Heraussuchen vieler alter Datenbanktabellen zu aufwendig erschien, hatten wir die Idee, dass die Gewinne wahrscheinlich mit unserem /post-System verschickt worden waren. Der /post-Befehl hat den Vorteil, dass er das Item nicht ins Spielerinventar legt, das möglicherweise voll sein kann, sondern in ein virtuelles Postfach, aus dem es der Spieler zu einem passenden Zeitpunkt abholen kann. Wer gerade im Nether unterwegs ist, wo jederzeit ein Sturz in die Lava droht, möchte nicht ausgerechnet jetzt einen wertvollen Gewinn in sein Inventar gelegt bekommen.

Den /post-Befehl gibt es seit Dezember 2015, er würde also für alle gesuchten Events infrage kommen. Ich müsste nur noch in unserem Befehls-Log die /post-Befehle zählen, die der Spieler "Unlimitedworld" zum Verschicken der verschiedenen Gewinne benutzt hat.
Leider ergab die Suche keinen einzigen Treffer. Denn wir hatten die Befehle nicht über den Chat eingegeben, was bei über hundert Gewinnern pro Event auch zu mühsam gewesen wäre, sondern direkt per Script ausgeführt. Dadurch landeten die Befehle nicht im Befehls-Log.

Hier endete vor drei Jahren der mehrstündige Aufwand zum Ermitteln der Eventzahlen. Im UW-Wiki musste ich bei den entsprechenden Köpfen die Anzahl "unbekannt" eintragen.

BLOBa.png


Ein BLOB stellt sich in den Weg​

Diese Lücke in der Kopfliste störte mich. Immer mal wieder fragte ich die Devs, ob es nicht eine andere Möglichkeit gäbe, an die Eventzahlen zu kommen. Dabei kreisten unsere Überlegungen um das /post-System. Jeder verschickte Gewinn landet in einem Spielerpostfach, was nichts anderes ist, als eine Datenbanktabelle mit folgenden Spalten: fortlaufende Nummer (id), UUID des Empfängers (target), UUID des Absenders (sender), Inhalt (data), Zeitstempel der Absendung (sent) und Zeitstempel der Abholung (received). Ich stellte mir vor, in dieser Tabelle einfach nach den Namen der betreffenden Deko-Köpfe zu suchen: Wie oft hat Absender "Unlimitedworld" den Deko-Kopf "XY" verschickt?

BLOBb.png


Aber so einfach ist das nicht, denn die Items werden in der Datenbank als BLOB gespeichert. Das ist die Abkürzung für binary large object. In diesem Format werden in einer Datenbank ganze Dateien abgelegt, z.B. pdf-Dokumente oder in unserem Fall komprimierte Item-Dateien mit sämtlichen Item-Daten wie Name, Textur, Anzahl, Beschreibungstext und sonstige Eigenschaften. Nach einem binären BLOB kann man in der Datenbank nicht suchen.

So sieht ein Item-BLOB aus, wenn man ihn mit einem Texteditor öffnet:

BLOBc.png


Wenn man weiß, dass Item-BLOBs komprimiert sind, kann man schon etwas mehr damit anfangen. Dach dem Entzippen zeigt ein Texteditor das an:

BLOBd.png


Insider erkennen darin die NBT-Daten des Items. NBT ist das interne Minecraft-Datenformat (named binary tag). Das Programm "NBTExplorer" (Quelle) zeigt solche Daten strukturiert und lesbar an.

BLOBe.png


Mit etwas Zeit und Geduld war es nun möglich, die Postfach-Tabelle manuell an den richtigen Zeitpunkten zu durchsuchen, verdächtige BLOBs zu extrahieren und herauszufinden, welches Item darin steckt. Ich ließ mir von den Devs einen Lesezugriff auf die Postfach-Tabelle einrichten und legte los.
Das erste fragliche Event war das Parkourevent vom Februar 2016. Es begann am 13.2.2016 um 18:00 Uhr. Im Forum steht, dass der erste Gewinner nach einer Stunde "Droidey" war. Die Tabelle sieht in diesem Zeitraum so aus:

BLOBf.png


Ihr seht mehrfach denselben Absender (ein Serverteamler) und immer dieselbe BLOB-Größe von 338 Bytes. Der erste Empfänger (post-ID 1219) ist ebenfalls ein Serverteamler, das war offensichtlich ein Test. Der zweite Empfänger (post-ID 1220) ist "Droidey". Bingo! Testweise holte ich mir mit einem Admin-Befehl das post-Item 1220 und erhielt den gesuchten Computermonitor! Hurra!

BLOBg.png


Interessanterweise bekam Droidey seinen Gewinn erst über zwei Stunden nach dem Start. Dann folgten im Sekundenabstand weitere sieben Gewinner - sicherlich noch manuell vergeben, erst später erledigte das eine programmierte Automatik.
Jetzt musste ich nur noch zählen, wie oft der Computermonitor verschickt worden war. Auch das war wieder leichter gesagt als getan, denn BLOBs kann man nicht suchen oder zählen. Aber SQL hat diverse Hilfsfunktionen, unter anderem die Funktion COUNT zum Zählen von Zeilen und die Funktion LENGTH für die Bytegröße eines BLOBs. Damit war mir folgende SQL-Abfrage möglich:

SELECT COUNT(*) FROM minecraft.post_items
WHERE sent >= '2016-02-13 18:00:00' AND sent <= '2016-02-21 00:00:00'
AND LENGTH(data) = 338

Als Ergebnis erhielt ich die Anzahl 111. So oft war der Computermonitor verschickt worden (inkl. dem ersten Test, also 110 Gewinner). Ich hatte es geschafft! Jetzt fehlten nur noch 26 weitere Köpfe, die ich auf diese Art ermitteln wollte.
Mit Halloween 2016, Ostern 2017, Ostern 2018, Ostern 2019, Sammelevent 2019 und Halloween 2019 kam ich gut voran:

BLOBh.png


Doch beim Halloween-Event 2017 gab es das Problem, das zwei Gewinne dieselbe BLOB-Größe hatten. Mit der length-Funktion konnte ich sie nicht unterscheiden. Die Devs gaben mir den Tipp, es mit der md5-Funktion zu versuchen. MD5 ist eine Funktion, die aus einer beliebigen Nachricht (z. B. einem BLOB) eine eindeutige Zeichenkette berechnet (ein sogenannter Hashwert). BLOBs mit gleicher Länge, aber unterschiedlichem Inhalt, ergeben unterschiedliche MD5-Werte. So konnte ich die BLOBs unterscheiden und auch diese Eventzahlen herausfinden.
Als letztes blieb das Geburtstagsevent 2018 übrig. Damals hatten wir ein Sammelevent in der Spawnzone veranstaltet. Wer 40 schwebende Kuchen gefunden hatte, bekam einen Deko-Kopf. Doch leider hatten wir den Gewinn nicht über das /post-System verschickt, sondern direkt ins Inventar gegeben. Um die Gewinnerzahlen herauszufinden, blieb in diesem Fall wirklich nichts anderes übrig, als in ein altes Datenbank-Backup zu schauen. Aber für ein einziges Event war das zeitlich vertretbar, sodass auch die letzte fehlende Eventzahl gefunden wurde.

BLOBi.png


Nun habt ihr nicht nur die Lösung für die 27 bisher unbekannten Eventzahlen erfahren, sondern nebenbei auch einen Einblick in das Thema "Datenbanken" bekommen und wisst, was ein BLOB ist und wie man mit ihm umgeht.

Fun Fact: Bei meiner Suche nach den Eventzahlen fiel mir auf, dass das Abholdatum bei einigen Postfächern leer war. Das bedeutet, diese Spieler haben ihren Gewinn bis heute nicht abgeholt, er schwimmt immer noch als BLOB in ihrem Postfach. *blubb*
 
Mich beeindruckt auch der enorme Aufwand, der für die Spieler gemacht wird. Nur um zu den Köpfen Zahlen zu haben. Vielen Dank!
Das wird zukünftig den Handel und die Kopfpreise bestimmt beeinflussen und wird eine große Hilfe sein um die Köpfe besser einzuschätzen.
 
Das wird zukünftig den Handel und die Kopfpreise bestimmt beeinflussen und wird eine große Hilfe sein um die Köpfe besser einzuschätzen.
Jedoch sollte man auch bedenken, dass nur weil ein Kopf z.B. 2016 X-Mal ausgeben wurde, deswegen nun nicht mehr so viele Köpfe im Umlauf sind. Viele der Leute den Kopf mal bekommen haben spielen wahrscheinlich schon lange nicht mehr und die Items sind weg. Letztendlich ist relevant, wie viele Köpfe davon "auf dem Markt" sind und auch kaufbar.
 
Jedoch sollte man auch bedenken, dass nur weil ein Kopf z.B. 2016 X-Mal ausgeben wurde, deswegen nun nicht mehr so viele Köpfe im Umlauf sind. Viele der Leute den Kopf mal bekommen haben spielen wahrscheinlich schon lange nicht mehr und die Items sind weg. Letztendlich ist relevant, wie viele Köpfe davon "auf dem Markt" sind und auch kaufbar.
Ja definitiv. Wie Sumpfhytte schrieb, wurden ja sogar einige der verschickten Köpfe nie aus dem Postfach entnommen.
Es ist dennoch ein kleiner Anhaltspunkt, an dem man sich zusätzlich zu anderen Eindrücken orientieren kann.

Aber es stimmt schon, dass es mehr auf andere Sachen wie die Verfügbarkeit ankommt. Manche Köpfe gibt es nur selten, die will aber trotzdem keiner haben, daher werden sie ständig verkauft :-D
 
Das ist wie beim sammeln von anderen Gegenständen auch.

Z.b. Münzen: Auflage 2000 Stück, wie viele davon verkauft worden sind x, wie viele davon wieder eingestanzt worden sind y. Nachfrage danach z, wann kommt eine Neuauflage :-X
 
  • Gefällt mir
Wertungen: SirHeimdall

Benutzer, die dieses Thema gerade lesen

ONLINE 29 Spieler