Discord

  • Es freut uns dass du in unser Minecraft Forum gefunden hast. Hier kannst du mit über 130.000 Minecraft Fans über Minecraft diskutieren, Fragen stellen und anderen helfen. In diesem Minecraft Forum kannst du auch nach Teammitgliedern, Administratoren, Moderatoren , Supporter oder Sponsoren suchen. Gerne kannst du im Offtopic Bereich unseres Minecraft Forums auch über nicht Minecraft spezifische Themen reden. Wir hoffen dir gefällt es in unserem Minecraft Forum!

Serialisieren von Itemstacks in Datenbanken - so einfach kann es gehen

JOO200

Vorarbeiter
Osterei Experte
Mitglied seit
18 Dezember 2016
Beiträge
257
Hallo zusammen,

ich hoffe, es passt hier rein. Nachdem ich mehrere Stunden mich mit der Serialisierung von ItemStacks rumgeschlagen habe (Die Blöden Dinger möchten einfach nicht in die Datenbank rein), habe ich schlussendlich doch eine erstaunlich einfache Methode gefunden.

Hintergrund: Ich möchte einige (private aber auch öffentliche) Plugins optimieren, zum Beispiel wäre doch ein ChestShop mit MySQL-Datenbanken ganz nett.

Idee: Ich benutze die Yaml-Methode, um ein ItemStack als YamlConfiguration abzuspeichern. Danach forme ich die YamlConfiguration in ein String um. Es ist zwar noch nicht perfekt, aber funktioniert bereits (Item in Datenbank reinschreiben, Item aus Datenbank lesen und in Inventar geben).

Vielleicht hilft das hier jemand anderen weiter. Der große Vorteil ist, dass man nicht über Abhängigkeiten speziell der Minecraft-Version gehen muss und die Items "lesbar" in der Datenbank stehen. Mutige Leute könnten daher sogar ihre Items in der Datenbank verändern und editieren.

Meine Tests haben zudem ergeben, dass die Serialisierung mit der hier vorgeschlagenen Yaml-Methode kürzer als die von ChestShop (-> Base64-codiert) ist.
In der MySQL-Datenbank war die entsprechende Spalte als "Text" deklariert.

Insert:
Code:
        ItemStack is = player.getInventory().getItemInMainHand();

            YamlConfiguration config = new YamlConfiguration();
            config.set("item", is);
            String serialized = config.saveToString();

            conn.insertString(serialized, e -> {
                // hier einfach in die Datenbank einsetzen. Aufgrund vorhandener Async-API ist das hier asynchron
                if(e != null) e.printStackTrace();
            });
Auslesen:
Code:
        conn.getFirstString((string, exception) -> {
                // asynchrone Abfrage
                if(exception != null) {
                    exception.printStackTrace();
                    return;
                }
                YamlConfiguration config2 = new YamlConfiguration();
                try {
                    config2.loadFromString(string);
                } catch (InvalidConfigurationException e) {
                    e.printStackTrace();
                    return;
                }
                Bukkit.getScheduler().scheduleSyncDelayedTask(this, () -> {
                    // player.getInventory().addItem() dürfte nicht threadsafe sein!
                    ItemStack is2 = config2.getItemStack("item");
                    player.getInventory().addItem(is2);
                });
            });
Und was soll ich sagen - sogar ganze Inventare lassen sich so in Datenbanken schreiben :D
Code:
            PlayerInventory inventory = player.getInventory();

            YamlConfiguration config = new YamlConfiguration();
            config.set("inventory", inventory);
            String ser = config.saveToString();

            conn.insertString(ser, e -> {
                if(e != null) e.printStackTrace();
            });
Das ganze muss man aber mit Vorsicht benutzen. Immerhin könnten bei einem vollen Inventar (z.B. mit beschriebenen Büchern) die Text-Länge nicht ausreichend sein.

Achso - in der Datenbank sieht es dann am Ende so aus:
Oben ein paar Itemeinträge, unten noch 2 Inventare von mir.


Vielleicht hilft es wem. Und vielleicht hat wer Verbesserungsvorschläge :)

Grüße Joo
 

BlackHole

Workaholic
Mitglied seit
1 Juli 2012
Beiträge
749
Minecraft
BlackHole
Ich würde bei MySQL den Datentyp "mediumtext" verwenden. Hier können die Einträge 16 MB groß sein. Das sollte für ein Inventar mit vollen Shulkerkisten ausreichen. Demgegenüber kann "text" nur 64 kB speichern.
 
F

Figz

Guest
Ich behaupte einfach mal, dass es niemals ohne Probleme funktionieren wird.

Um ehrlich zu sein, weiß ich jetzt nicht wie der Serializer das handelt, aber wenn man sich die letzten beiden Einträge aus deinem Bild anschaut, ist dort die Version (v1_10_R1) hinterlegt. Was passiert, wenn man nun auf eine andere Version umsteigt? Wird das automatisch beim Deserialisieren umgewandelt?
 

JOO200

Vorarbeiter
Osterei Experte
Mitglied seit
18 Dezember 2016
Beiträge
257
Um ehrlich zu sein, weiß ich jetzt nicht wie der Serializer das handelt, aber wenn man sich die letzten beiden Einträge aus deinem Bild anschaut, ist dort die Version (v1_10_R1) hinterlegt. Was passiert, wenn man nun auf eine andere Version umsteigt? Wird das automatisch beim Deserialisieren umgewandelt?
Dann muss ich wohl hinzufügen: Bei Spielerinventaren ist dies nicht einfach so möglich, während es bei normalen ItemStacks einwandfrei funktioniert ;)
 

JOO200

Vorarbeiter
Osterei Experte
Mitglied seit
18 Dezember 2016
Beiträge
257
Ich behaupte einfach mal, dass es niemals ohne Probleme funktionieren wird.

Um ehrlich zu sein, weiß ich jetzt nicht wie der Serializer das handelt, aber wenn man sich die letzten beiden Einträge aus deinem Bild anschaut, ist dort die Version (v1_10_R1) hinterlegt. Was passiert, wenn man nun auf eine andere Version umsteigt? Wird das automatisch beim Deserialisieren umgewandelt?
Ich möchte nochmal dieses Problem aufgreifen.
Da das Abspeichern in ein Yaml-String versionsabhängig ist, habe ich nach alternativen gesucht. Dabei gibt es Alternativen, auf die ich gestoßen bin:

Mit PlayerInventory$getArmorContents(), PlayerInventory$getContents() und PlayerInventory$getExtraContents() kann man das Inventar in Arrays mit ItemStacks zerlegen. Dies kann man entwder
  • komplett in die Yaml-Config setzen
  • oder den Slot-Index ermitteln und den Spieler + Slot-Index als Primary Key einstellen und das dann so in die Datenbank einpflegen.
Grüße Joo
 
Oben