• 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!

Auswählen von Blöcken durch draufschlagen nach Befehleingabe

AnonymusChaotic

Workaholic
Registriert
22 November 2013
Beiträge
760
Diamanten
0
Ich beginne gerade mit der Pluginentwicklung unter Bukkit/Spigot. Ich möchte, nachdem ich einen Befehl (als Spieler) abgesendet habe mehrere Blöcke durch daraufschlagen (selber Spieler) auswählen können. Bei der Auswahl sollen sofort gewisse überprüfungen durchgeführt werden, in etwa so:

/befehl
Server: "Schlage einen Wollblock"
Spieler: schlägt Stein -> Server: "falscher Block, schlage einen Wollblock"
Spieler: schlägt Wollblock
Server: Schlage einen Grasblock/Wähle eine Grasblock aus
Spieler: schlägt Grasblock
-> Server/Plugin tut irgendwas

Wie gehe ich hier am Besten vor?
Ich habe dazu folgende Idee:
  1. Command wird aufgerufen
  2. CommandExecutor Class, worin eine
  3. BlockBreakEvent-Methode enthalten ist (also ein BlockBreakEvent registriert wird)
  4. Plugin schickt Spieler Nachrichten und kontrolliert/speichert zerbrochene Blöcke, cancelled das Event
  5. Wenn fertig -> Event unregistrieren [BlockBreakEvent.getHandlerList().unregister(Plugin plugin)???]
  6. Server tut irgendwas (baut basierend auf den ausgewählten Blöcken ein Haus)
Ist das so richtig?
Gibt es bessere Wege, das gleiche zu erreichen?
Ist BlockBreakEvent das richtige Event oder ist BlockDamageEvent besser geeignet (Javadocs sind hier wenig hilfreich)?
Kann ich feststellen, ob ein Event tatsächlich unregistriert wurde? Muss ich das Event tatsächlich selbst unregistrieren, oder wird das, wenn das Objekt, das das Event registriert hat nicht mehr gebraucht wird, von selbst unregistriert?

Und zu guter Letzt noch eine andere Frage: Man muss immer wieder das Plugin einer Methode übergeben [Wie etwa in ... .getHandlerList().unregister(Plugin plugin)].
Kann ich das Plugin auch auf andere Art und Weise bekommen, als allen Methoden und Objekten, wo ich es brauche von der "Basisklasse", also jene die "extends JavaPlugin" ist, "durchzugeben"?

Vielen Dank für Antworten!
 

Chrisliebär❤️

nur echt mit ❤️
Moderator
Registriert
19 Mai 2014
Beiträge
1.675
Diamanten
830
Da du an einer wohl durchdachten Antwort interessiert scheinst eine kurze Frage: Was genau willst du damit erreichen? Ist das ein Minigame? Oder anders: Führt jeder Spieler auf dem Server diese Aufgabe aus? Führen alle Spieler gemeinsam die selbe Aufgabe aus? Gibt es Spieler die so eine Aufgabe nie ausführen, während andere Spieler eine Aufgabe ausführen?

Und zuletzt: Soll die Aufgabe bereits beim ersten Schlag auf den Block oder erst bei dessen Zerstörung abgeschlossen sein?
 

BlackHole

Workaholic
Registriert
1 Juli 2012
Beiträge
752
Diamanten
0
Minecraft
BlackHole
Erstelle eine eigene Klasse, die sich um die Verwaltung kümmert. Diese rufst du dann vom Befehl und von den entsprechenden Listenern auf.
Diese Klasse muss die Daten zum Spieler geeignet speichern.

Das geeigneste Event wenn Spieler auf Blöcke klicken wäre das PlayerInteractEvent. Registriere es einmalig beim Start des Plugins. Deine Klasse muss dann prüfen, ob der Spieler gerade behandelt werden muss.
BlockBreakEvent wird aufgerufen, wenn ein Block vollständig abgebaut wurde, BlockDamageEvent wenn der angehauen wird und sich diese Risse bilden.

Statt das Plugin immer durchzureichen gibt es z.B. das Entwurfsmuster Singleton.
 

AnonymusChaotic

Workaholic
Registriert
22 November 2013
Beiträge
760
Diamanten
0
@可愛い,
Danke für die Antwort; hier der Hintergrund:
Der Sinn des Plugins ist, eine Grundform eines Hauses (aus Wolle gebaut, mit darauf liegenden Steinen bzw. Glasblöcken), zwei Säulen (eine mit Glasblöcken -> Fenster, eine ohne -> Wand) zu erkennen. (Siehe Bild)
2017-08-25_04.29.10.png

Dazu soll einmal die Fenstersäule und einmal auf die Wandsäule sowie zuletzt auch die Wolle markiert werden.
Das Plugin sucht dann (in Schritt 6 vom Startpost) alle grünen zusammenhängenden Woll-Blöcke, setzt die Säulen reihum und baut oben ein Dach* drauf.

Diese Aktion wird nur von wenigen Spielern (etwa 1-max. 5; vielleicht pro Spieler alle halben Stunden ein mal) am Server ausgeführt. Der Server hat normalerweise kaum bis keine weiteren Spieler. Trotz der relativ seltenen Ausführung möchte ich lernen, Plugins möglichst Ressourcen-schonend zu entwickeln.

@BlackHole
Erstelle eine eigene Klasse, die sich um die Verwaltung kümmert. Diese rufst du dann vom Befehl und von den entsprechenden Listenern auf.
Diese Klasse muss die Daten zum Spieler geeignet speichern.

Das geeigneste Event wenn Spieler auf Blöcke klicken wäre das PlayerInteractEvent. Registriere es einmalig beim Start des Plugins. Deine Klasse muss dann prüfen, ob der Spieler gerade behandelt werden muss.
...
Ebenfalls Danke für Deine Antwort.
Was meinst du mit "um die Verwaltung kümmert" und "Daten zum Spieler geeignet speicher"t? Ich kann Daten ja nur sinnvoll speichern, indem ich ein Objekt erstelle (außer ich schreibe es in eine Datei/Datenbank). Auf Objekte kann ich von zwei verschiedenen Stellen des Programmcodes nicht einfach so zugreifen?

Wie Ressourcenhungrig ist es, ein PlayerInteractEvent permanent registriert zu haben und bei jedem Klick Dinge prüfen zu müssen?




----------------------------------
* Einen Algorithmus zur Konstruktion des Daches habe ich bereits entwickelt, muss nur noch implementiert werden.
 

Chrisliebär❤️

nur echt mit ❤️
Moderator
Registriert
19 Mai 2014
Beiträge
1.675
Diamanten
830
Also mir ist jetzt nicht klar, warum der Spieler nach einem Wollblock noch einen weiteren Block auswählen muss (ich vermute mal um Fenster oder Wand zu setzen), wenn bereits das Auswählen des Stein oder Glasblocks reichen würde. Aber ich gehe jetzt mal davon aus, das hat einen Grund.

Ich versteh das so, dass jeder Spieler unabhängig von den anderen Spielern agiert, daher nehme ich an, du speicherst alles was ich hier nenne pro Spieler. Dazu musst du dir bei allen Events die Quelle (also den Spieler) holen und dann über eine Hashmap oder ähnliches Daten mit dem Spieler speichern, darauf gehe ich jetzt mal nicht ein. Bedenke nur, dass du die Spielerdaten auch aufräumen musst, wenn der Spieler den Server verlässt.

Was du vermutlich suchst nennt sich Statemachine (https://de.wikipedia.org/wiki/Endlicher_Automat sehr Mathematisch). Die Idee ist, dass jede Instanz (also jeder Spieler) sich in einem bestimmten Zustand befindet. Beispielsweise "Muss Wolle schlagen", "muss Stein/Glass schlagen", "muss Befehl eingeben". Man kann auch den Vergangenen Zustand nehmen "hat Wolle geschlagen", "hat Stein geschlagen", aber das ist in der Regel nicht so handlich.

Im Code kannst du dir das ganz einfach speichern, du erzeugst dir eine Variable, die den Zustand speichert und baust eine Logik, die abhängig vom aktuellen Zustand eine Aktion ausführt und ggf. den Zustand ändert. In schrecklichem Pseudocode sieht das ungefähr so aus
Code:
enum State {INACTIVE, WOOL_EXPECTED, TYPE_BLOCK_EXPECTED}

// pro Spieler speicherst du
State currentState = ...;

// in dem Event, welches den geschlagenen Block registriert
function onBlockHit (event) {
   if not event.isplayer return;

   // spielerobjekt irgendwo her holen (hashmap, etc.)
   playdata = foobar.getPlayerData(event.source);

   // abhängig von zustand aktion ausführen
   switch(playerdata.currentState){
     case INACTIVE:
       error(du musst erst /befehl ausfuehren) // sofern du eine fehlermeldung willst
       return

     case WOOL_EXPECTED:
       if event.block == wool
         // dein Code fuer glass

         // naechsten zustand setzen
         playerdata.currentState = State.TYPE_BLOCK_EXPECTED

         return;
       else
         error(du musst zuerst wolle schlagen)
         return;

     case TYPE_BLOCK_EXPECTED:
       if event.block == glass
         // code fuer glass

         // keine Ahnung was danach passieren soll, ich vermute nochmal wolle schlagen?
         playerdata.currentState = WOOL_EXPECTED;

         return;
       else if event.block == stone
         // code fuer stone

         // keine Ahnung was danach passieren soll, ich vermute nochmal wolle schlagen?
         playerdata.currentState = WOOL_EXPECTED;

         return;
       else
         error(du musst entweder wolle oder glass schlagen)
         return;

     // default case fuer programmierfehler
     case default:
       log.error(invalid case detected, fix your shitty code)
   }
}

// in command handler fuer deinen befehl(e) musst du dann selbst gucken was du machst, irgendwie sowas vermutlich
function onCommand(event)
   // spielerobjekt irgendwo her holen (hashmap, etc.)
   playdata = foobar.getPlayerData(event.source);

   if event.command ==enable
     playerdata.currentState = WOOL_EXPECTED;
   if event.command == disable
     playerdata.currentState = INACTIVE;
   else
     error(unknown command)

// und fuer den spielerzustand koenntest du noch sowas machen
class MyPlayerData {
   State currentState...
   int foo = 2;
   int bar = 5;
   Location myLoc =...;
}


// erzeuge player daten bei join
function onPlayerJoin(player)
   MyPlayerData data = new MyPlayerData;

   MyPlugin.getInstance().playermap.put(player, data);


// loesche player daten
function onPlayerQuit(player)
   MyPlugin.getInstance().playermap.remove(player)



// und in der plugin klasse
Map<Player, MyPlayerData> playermap = new IdentityHashMap<>();

hierzu lesen: https://docs.oracle.com/javase/8/docs/api/java/util/IdentityHashMap.html
(Für den Grasblock am Ende brauchst du einfach einen weiteren Zustand, ich denke es ist klar, wie das System funktioniert.)

Ich hoff das Beispiel illustriert die Idee ein bisschen. Welche Zustände du genau brauchst und in welchen Zuständen du etwas tun musst, hängt stark davon ab, wie sich das Plugin verhalten soll.

Zu den Fragen, wie du das nun in Bukkit implementierst. Sofern du kein Event behandelst, dass nicht mehrfach pro Sekunde ausgeführt wird, brauchst du dir oft nicht all zu große Gedanken machen. Wichtig ist, dass du im negativen Fall, also wenn dein Plugin nichts zu tun hat, die Methode schnell beendest. Es macht zum Beispiel keinen Sinn den Block eines Events zu prüfen, wenn der Spieler aktuell gar kein Haus baut. Es macht auch keinen Sinn das Block Event zu löschen, nur weil gerade kein Spieler das Plugin nutzt, das sind Optimierungen, die einfach in der Masse von größerem Code untergehen und den Code schlecht lesbar machen. Ich würde auch grundsätzlich anstreben alle Listener beim Start deines Plugins zu registrieren, sofern es nicht einen WIRKLICH GUTEN Grund gibt, dass sie dynamisch registriert werden. Das sollte auch dein Problem mit der Plugininstanz größtenteils lösen. Es macht an der Stelle auch keinen unterschied ob du 5 oder 500 Spieler hast. Das ist erstmal grundsätzlich ein sehr guter Stil.

Entscheidend ist eigentlich immer nur welcher Code in einem Event ausgeführt wird, wenn es nichts tut. Aber auch hier solltest du sehr vorsichtig sein, wenn du etwas optimierst. Viele Optimierungen, die die Befehlsreihenfolge betreffen werden auch vom Compiler durchgeführt, beispielsweise folgender Code.
Code:
playerData = getPlayerDataFrom() // dauert vielleicht ein bisschen

switch(n)
   case 1:
     player.money = 20;
   case 2:
     player.money = 40;
   case 3:
     server.sendmessage(wuuzzaaa)

Case 3 braucht den Wert von playerData gar nicht. Es ist daher naheliegend den Code in Case 1 und Case 2 zu kopieren. Der Compiler erkennt solche Fälle aber in der Regel selbst und erzeugt selbst duplizierten Code für die zwei Ausführungszweige. Dabei bleibt der eigentliche Quellcode aber gut lesbar.

@BlackHole hat dir ja schon grundsätzlich erklärt, wie du die Plugininstanz im Code nutzen kannst. Was du dabei aber nur bedenken solltest ist, dass ein übermäßiger Zugriff auf ein Singleton in aller Regel ein Zeichen von Codesmell ist. (https://de.wikipedia.org/wiki/Smell_(Programmierung) Objekte, die man sehr oft im Code verwendet sind zudem of Gottobjekte (https://de.wikipedia.org/wiki/Gottobjekt). Nun kannst du da nicht immer etwas dagegen tun, da du keinen Einfluss darauf hast, wie die Bukkit API aufgebaut ist, aber dir sollte zumindest das Problem bewusst sein. Spätestens wenn du deine Plugininstanz 2-3 Stufen tief durchreichst, solltest du überlegen ob es nicht andere Möglichkeiten gibt.

So kannst du z.B. deine Pluginklasse zuerst die Objekte initialisieren lassen und sie dann erst nachträglich einander bekannt machen. Das sind aber Fragen von gutem Softwaredesign, da gibt es nur sehr wenig objektive Möglichkeiten eine Lösung zu bewerten.

Kann ich feststellen, ob ein Event tatsächlich unregistriert wurde? Muss ich das Event tatsächlich selbst unregistrieren, oder wird das, wenn das Objekt, das das Event registriert hat nicht mehr gebraucht wird, von selbst unregistriert?
Wenn du den Listener deregistrierst, wird er nicht mehr über Events benachrichtigt. Die Pluginarchitektur von Bukkit ist hier leider totaler Murks, wesshalb Bukkit so ziemlich gar nichts automatisch wegräumt (Listen und Tasks glaub allerdings schon). In einer idealen Welt, müsstest du in der onDisable Methode dafür sorgen, dass sich dein Plugin restlos aus dem Server ausklingt, alle Listener löscht, alle Timer, Tasks und Threads beendet und alle Daten speichert. Würde sich jedes Plugin an diese Regel halten, so würde der /reload Befehl auch das tun was er verspricht. Da aber so ziemlich kein einziges Plugin das versucht, brauchst du dich ums Aufräumen fast nicht zu kümmern. Du kannst davon ausgehen, dass der Server nach dem onDisable beendet wird. Von daher musst du lediglich deine Daten sichern.

Java nutzt dynamische Speicherverwaltung. Ein Objekt, "dass du nicht mehr benutzt" kann trotzdem weiter existieren. Man kann da zwar ein wenig Voodoo machen, aber das lassen wir an der Stelle besser mal. Wenn du das genauer verstehen willst, solltest du vielleicht C oder auch C++ lernen, dann wird dir klar, dass irgendwas die Objekte irgendwann löschen muss, denn wie du sicherlich auch gemerkt hast, gibt es in Java keine Möglichkeit irgendwelche Objekte zu löschen. In C/C++ bist du dafür verantwortlich, in Java gibt es ein automatisches System, das irgendwann nach Objekten sucht, die nicht mehr genutzt werden. (http://javapapers.com/java/how-java-garbage-collection-works/) Die genaue Implementierung kann von JVM zu JVM unterschiedlich ausfallen. Die Idee ist allerdings immer die, dass ein Objekt erst dann gelöscht wird, wenn es über keine Referenzen aus einem "Root" erreichbar ist. "Root" sind verschiedene Objekte und Strukturen. Beispielsweise Threads oder statische Attribute. Wenn du in deinem Plugin die Referenz auf einen Listener auf null setzt, so ist noch immer eine Referenz im Event System von Bukkit gespeichert und wird daher nicht gelöscht. Daher werden Listener auch nicht automatisch gelöscht, das ist nämlich gar nicht möglich. Jedenfalls nicht so wie du dir das vorstellst.
 
Zuletzt bearbeitet:

BlackHole

Workaholic
Registriert
1 Juli 2012
Beiträge
752
Diamanten
0
Minecraft
BlackHole
Minecraft geht nicht gerade sparsam mit Resourcen um. Entsprechend musst du auch nicht darauf achten, dass dein Plugin besonders resoucenschonend ist. Bei guter Programmierung geht weniger Resourcenverbrauch einher mit mehr Bedarf an Rechenleistung.

Ständiges registrieren und Deregistrieren von Listern und das pro Spieler ist aufwändiger als einen Listener einmalig zu registrieren. Unter bestimmten Umständen kann das auch zu Speicherlecks führen: https://github.com/PaperMC/Paper/issues/786
Die Prüfung ob ein Spieler behandelt werden muss, kann sehr effizient umgesetzt werden, indem die Handlerklasse z.B. prüft, ob die UUID des Spielers in einem bestimmten Set enthalten ist. Vorher kannst du bereits prüfen, ob es sich z.B. um einen Linksklick auf einen Block handelt. Wie bereits von Vorposter geschrieben, schließt man zuerst das aus, was sich am einfachsten prüfen lässt und dann das, was am meisten ausschließt. Danach können immer detaillierte Prüfungen kommen.

Die Klasse in der die ganze Logik abläuft und die von Befehl und Listerner aufgerufen wird, könntest du z.B. alle Felder und Methoden als static definieren. So ein Codedesign ist zwar nicht optimal, liegt aber immer noch weit über dem Durchschnitt der Codequalität bei Bukkit-Plugins oder auch professionellen Softwareprojekten. ;)
Ich glaube aber, dass du noch einiges an Grundwissen von Java und Programmiertechniken allgemein benötigst. Ich würde dir empfehlen, erst einmal dieses kostenlose und sehr verständliche Ebook durchzuarbeiten:
http://openbook.rheinwerk-verlag.de/javainsel/
 

AnonymusChaotic

Workaholic
Registriert
22 November 2013
Beiträge
760
Diamanten
0
Wow, Vielen Dank für die ausführlichen Antworten. Da stehen Dinge, die ich teilweise noch nicht so ganz verstehe. Ich werde den vielen Hinweisen folgen und erst mal gaanz viel lernen :) (und die Vorlesungen Datenmodellierung und Formale Modellierung im kommenden Semester besuchen, da wird das nämlich irgendwann mal unterrichtet). Wenn ich dann ein ungefähres Verständnis habe und den entsprechenden Teil des Plugins geschrieben habe, melde ich mich diesbezüglich wieder. (Oder wenn ich vor Fragen stehe, die ich nicht selbst lösen kann)

In der Zwischenzeit habe ich ein Plugin* geschrieben, das ein Dach baut an genau der Stelle, auf die man bei der Eingabe des Befehls
/build roof <Material> <Dachhöhe>
Dabei muss auf eine gerade Fläche gesehen werden, die auf allen Seiten von einem mindestens 1 Block tiefen Graben umgeben ist.
Der Algorithmus ist darauf ausgelegt, jede beliebige Hausform decken zu können.

Ich würde mich freuen, wenn jemand einmal einen Blick darauf wirft :)
Github-Repo: FastBuild Plugin oder ZIP-Datei im Anhang.


* Eigentlich ein Teil des oben beschriebenen Plugins
 

Anhänge

  • FastBuild-master.zip
    18,4 KB · Aufrufe: 148
Zuletzt bearbeitet:

BlackHole

Workaholic
Registriert
1 Juli 2012
Beiträge
752
Diamanten
0
Minecraft
BlackHole
Bei deinem Befehl fehlt die Prüfung auf die richtige Argumentanzahl. Wenn das erste Argument "roof" ist werden z.B. mindestens 3 Argumente benötigt, ansonsten gibt es eine Exception. Material.matchMaterial() erzeugt keine NullPointerException, sondern gibt einfach null zurück, falls kein passendes Material gefunden wurde. Der Spieler könnte auch in die Luft schauen. In diesem Fall würde getTargetBlock() null zurück liefern.

Bei BuildRoof könntest du direkt im Konstruktor sicherstellen, das player null ist, dann musst du das nicht in den Methoden machen. Der Algrithmus selber sieht recht unübersichtlich aus, wird aber vermutlich funktionieren. Schaue dir mal https://de.wikipedia.org/wiki/Floodfill an.
 

Chrisliebär❤️

nur echt mit ❤️
Moderator
Registriert
19 Mai 2014
Beiträge
1.675
Diamanten
830
Wow, Vielen Dank für die ausführlichen Antworten. Da stehen Dinge, die ich teilweise noch nicht so ganz verstehe. Ich werde den vielen Hinweisen folgen und erst mal gaanz viel lernen :) (und die Vorlesungen Datenmodellierung und Formale Modellierung im kommenden Semester besuchen, da wird das nämlich irgendwann mal unterrichtet). Wenn ich dann ein ungefähres Verständnis habe und den entsprechenden Teil des Plugins geschrieben habe, melde ich mich diesbezüglich wieder. (Oder wenn ich vor Fragen stehe, die ich nicht selbst lösen kann)
Du brauchst den verlinkten Artikel nicht verstehen. Wie du das implementierst hab ich dir bereits als Pseudocode geschrieben. Ich wollts nur der Vollständigkeit halber erwähnen. Wenn du Google nach "switch case state machine" fragst findest du ähnlichen Code.

Ich würde mich freuen, wenn jemand einmal einen Blick darauf wirft :)
Github-Repo: FastBuild Plugin oder ZIP-Datei im Anhang.
Habs nur überflogen, hab nicht wirklich gerade die Zeit mir zu überlegen was der Code tut. Große Teile des Codes sind nicht gut dokumentiert (https://github.com/MinecraftVienna/...meczek/dev/fastbuild/build/BuildRoof.java#L74 https://github.com/MinecraftVienna/...eczek/dev/fastbuild/build/BuildRoof.java#L112). Andernfalls wärs vielleicht einfacher sich schnell einen Überblick zu beschaffen. Die Methoden selbst sind vor allem nicht dokumentiert, das ist das größte Problem, schau dir mal Javadoc an, falls dir das nichts sagt. Das hilft nicht nur anderen dabei deinen Code zu verstehen, sondern macht es auch möglich, dass du deinen eigenen Code noch in ein paar Monaten erkennst. Formatierung ist übrigens sehr merkwürdig. Teilweise zufällig platzierte Zeilenumbrüche und sowas. Kann man zwar machen aber ist sehr selten.

Ansonsten nehm ich an, dass dein Code funktioniert, ist jetzt auch nicht besonders umfangreich. Sieht halt schon mal komisch formatiert aus. Erfahrungsgemäß ist Code, der so zersplittert aussieht auch fehlerhaft ;) Ansonsten kannst du aber auch fragen, wenn es bei der Umsetzung vom Plugins Probleme gibt.
 

AnonymusChaotic

Workaholic
Registriert
22 November 2013
Beiträge
760
Diamanten
0
@BlackHole
Danke für die Hinweise, ich prüfe jetzt auf die Anzahl der Argumente.
Für die Exceptions habe ich in der Spigot/Bukkit Dokumentation nichts gefunden. Gibt es da irgendwelche Informationsquellen, welche Methoden zu welchen Exceptions führen können oder wirft Bukkit nie Exceptions?

Player.getTargetBlock() gibt übrigens nie null zurück, sondern dann einfach eine location irgendwo in der Luft, wenn man in die Luft schaut.

Bei BuildRoof könntest du direkt im Konstruktor sicherstellen, das player null ist, dann musst du das nicht in den Methoden machen.

Player setzte ich nie null, außer bei einem Konstruktor ohne Spieler. Die Idee war, diese Klasse so zu bauen, dass sie theoretisch auch ohne Spieler (also von der Konsole/CommandBlock aus) verwendet werden kann. Daher gab es auch die Möglichkeit einen leeren Konstruktor aufzurufen.
Ich habe das jetzt aber gelassen, ist nicht notwendig.

Theoretisch könnte man jetzt immer noch null als Spieler übergeben, dann gibt das halt irgend ein unerwartetes Ergebnis. Nicht optimal, aber ich weiß grad nichts besseres.

Der Algrithmus selber sieht recht unübersichtlich aus, wird aber vermutlich funktionieren. Schaue dir mal https://de.wikipedia.org/wiki/Floodfill an.

Vielen Dank für den Hinweis auf diesen Algorithmus. Im wesentlichen hatte ich Floodfill 4-connected oder 4-Neighbour bereits implementiert (ohne zuvor von diesem Algorithmus gehört zu haben) , allerdings etwas abgewandelt (mit Tests vor der Rekursion statt am Kopf der Methode. Die Wikipedia-Seite hat mir geholfen, das etwas schöner zu implementieren
Außerdem hatte ich zahlreiche Ausgaben in die Konsole zwecks Debugging, die ich ebenfalls entfernt habe.

@可愛い
Danke für die erneut sehr ausführliche Antwort, ich mache mich dann in den nächsten Tagen an die Implementierung. Davor muss ich mir das mit playerdata und enum noch ansehen (und Docker lernen und einen Server (neu) einrichten ;) ).


Das mit dem Hinweis auf die Dokumentation war gut, in ein paar Wochen würde sonst wohl das für mich gelten:
Code:
//When I wrote this, only God* and I understood what I was doing
//Now, God* only knows
Ich hoffe es ist besser geworden :)
Ich hatte im Code noch zahlreiche Ausgaben zum Debuggen, die ich eigentlich schon letztens hätte entfernen sollen. Naja, jetzt sind sie weg :)
Was Du mit zersplittertem Code und unnötigen Zeilenumbrüchen meinst weiß ich nicht. Vielleicht sind die aber bei meinen vorherigen Änderungen verschwunden? Die einzigen "zufällig" platzierten Zeilenumbrüche sind an jenen Stellen, wo if, return, ... - Stücke zu lange werden. Dann schreibe ich sie untereinander, um eine halbwegse Lesbarkeit zu ermöglichen.

Vielen Dank für die Tipps!

Wen es noch Sachen gibt, die ich besser machen kann freue ich mich über Hinweise. Es ist mein Ziel, den bestmöglichen Code zu schreiben!

Und nochmal der Github-Link

*Ohne religiösen Bezug, nur als Witz gemeint
 
Zuletzt bearbeitet:

BlackHole

Workaholic
Registriert
1 Juli 2012
Beiträge
752
Diamanten
0
Minecraft
BlackHole
Die Dokumentation zu Spigot ist recht mangelhaft. Die Java-Dokumentation ist für viele Methoden gar nicht vorhanden oder listet nicht alle möglichen Exceptions auf. Allgemein ist aber davon auszugehen, dass eine Methode eine NullPointerException erzeugt, falls für ein Argument einen Null-Wert angibt, der nicht null sein sollte. Teilweise wird stattdessen aber auch eine IllegalArgumentException erzeugt. Diese erhält man auch, wenn ein Argument außerhalb des Gültigkeitsbereich ist, also z.B. ein String länger ist eine bestimmte Maximallänge oder man eine negative Zahl an einer Stelle übergibt, an der nur positive Zahlen erwartet werden.
Die Dokumentation zu Material.matchMaterial() sagt aber: "Returns: Material if found, or null"
 
Oben