Willkommen,
Gast
|
|
Hallo zusammen,
ich wollte nun auch mal was über mein Bastelprojekt erzählen, in diesem Fall ist es der "Minecraft Bot". Einige von euch kennen diesen wahrscheinlich schon als den Spieler "EOSGlaDOS" auf unserem Minecraft Server. Erstmal zum Format von dem hier: Ich habe vor das wie einen kleinen Blog aufzubauen, dabei werde ich in diesem Beitrag erst einmal bisschen allgemein was dazu sagen. In den folgenden Beiträgen werde ich dann Stück für Stück auf einzelne Dinge eingehen und dabei versuchen so wenig Programmierkenntnisse wie möglich voraus zu setzen. Dennoch werde ich auf ein paar Details eingehen, womit ich euch ein bisschen Wissen über Softwareentwicklung und Netzwerktechnik vermitteln möchte. Ihr dürfte gerne zwischen den Beiträgen schreiben, ich werde die einzelnen Beiträge untereinander Verlinken, sowie in diesem hier eine Übersicht erstellen. Übersicht: - Teil 1 <dieser hier> - Teil 2 - Wie alles begann So, also erst mal zu der Frage: Was ist dieser Spieler Namens "EOSGlaDOS" überhaupt? Dieser Spieler ist ein von mir geschriebenes Programm, welches das Minecraft Netzwerkprotokoll spricht und damit auf prinzipiell jeden Minecraft Server verbinden kann. Im Gegensatz zum normalen Minecraft Client (damit meine ich das Java Programm, mit welchem man üblicherweise spielt) hat dieses Programm jedoch keine Benutzeroberfläche, sondern ist eine reine Konsolenanwendung. Ihr könnt euch das als Programm vorstellen, welches man beim starten sagt, wohin es verbinden soll und danach läuft es einfach. Jetzt fragt ihr euch wahrscheinlich, welchen Sinn den so ein Programm haben soll, kann man ja nicht viel mit tun. Das ist soweit auch korrekt, mit dem Programm alleine kann man nicht all zu viel tun. An dieser Stelle kommt jedoch der Modulare Aufbau zum tragen. Mein Projekt besteht dabei aus mehreren "Modulen", das eben erwähnte ist dabei das Hauptmodul, es verbindet sich zu einem Minecraft Server, interpretiert dabei alle anfallenden Daten und behält diese im Speicher. Gleichzeitig bietet es eine Schnittstelle für weitere Module, mit welchen man dann Schlussendlich etwas steuern kann. Hier mal der Versuch einer Skizze: ----------- -------------- --------------------
| Modul 1 |------| Hauptmodul |------| Minecraft Server |
----------- -------------- --------------------
|
----------- |
| Modul 2 |-------------
----------- Der Sinn und die Idee hinter dem ganzen ist, dass das Hauptmodul immer laufen kann, während die einzelnen Module beliebig zugeschaltet und beendet werden können. Dies hat einerseits den Vorteil, dass nicht immer alles laufen muss, also z.B. die Grafikkarte nicht damit beschäftigt wird eine Welt anzuzeigen wo gerade keiner hinschaut, andererseits macht es aber auch die Entwicklung deutlich einfacher. So können diese einzelnen Module unabhängig voneinander entwickelt werden. Und auch wenn eines mal aufgrund eines Fehlers abstürzt (ja, so was kommt häufiger vor während man entwickelt..), läuft das restliche System in der Regel unbeschadet weiter. Ebenfalls erlaubt es dieser Aufbau, dass die einzelnen Module auf verschiedenen Rechnern laufen können. Die gestrichelte Verbindungen zwischen den Modulen und auch zu dem Minecraft Server stellen jeweils eine Netzwerkverbindung dar (auf diese möchte ich im nächsten Beitrag genauer eingehen). Wer sich schon mal die Frage gestellt hat, ob ich den die ganze Zeit meinen Rechner laufen habe, nur damit der Bot quasi-Permanent auf den Server bleiben kann. Nicht ganz: Aktuell läuft das Hauptmodul auf einem Raspberry Pi (siehe Wikipedia). Ein Raspberry Pi ist ein kleiner Mini-Computer, welcher aus ähnlicher Hardware gebaut ist wie heute übliche Smartphones. Soll heißen: Ich habe hier einen kleinen Einplatinenrechner mit 700Mhz CPU Takt und 512MiB RAM am laufen, welcher sich mit etwa 2W Stromleistung begnügt. Auf diesen läuft, neben ein paar anderen Projekten von mir, über welche ich vielleicht auch mal was schreiben werde, eben dieses Minecraft Hauptprogramm. Ebenfalls ein Vorteil dieser Aufteilung ist, dass die einzelnen Module in verschiedenen Programmiersprachen geschrieben sein können. Dem ein oder anderen von euch ist sicher bekannt, dass sich einzelne Programmiersprachen verschieden gut für bestimmte Anwendungsfälle eignen. Es gibt Sprachen, welche sich zum Beispiel sehr gut eignen um Benutzeroberflächen zu bauen, als Beispiele möchte ich hier mal C# mit .NET sowie Delphi erwähnen. Andere Sprachen wie z.B. Python eignen sich hingegen sehr gut um "schnell" kleine Dinge umzusetzen. Ohne tiefer in die Details einzugehen, möchte ich damit ausdrücken, dass manche Programmiersprachen für bestimmte Problemstellungen besser geeignet sind als andere. Also eine bestimmte Funktionalität mit verhältnismäßig wenig Programmcode umsetzbar ist. Dies soll jedoch nicht bedeuten, dass manche Dinge mit einer Sprache nicht umsetzbar wären, nur können diese mehr Aufwand erfordern. Natürlich gibt es auch Programmiersprachen, welche sich aufgrund von Beschränkungen für manche Problemstellungen quasi gar nicht eignen, aber darum soll es hier nicht gehen. In diesem Projekt habe ich das Hauptmodul, welches die meisten Verwaltungsaufgaben übernimmt, komplett in C++ geschrieben. Die einzelnen Module, wovon es mittlerweile auch eine ganze Menge gibt (später dazu mehr), sind in Python, Blitzbasic, C++ und eines auch in PHP umgesetzt. So, so viel mal im ersten Beitrag. Im nächsten werde ich mal von vorne Anfangen und bisschen darüber erzählen, wie das Projekt überhaupt begonnen hat. Weiter zu Teil 2. |
Letzte Änderung: 06 Okt 2016 19:34 von Tirus. Begründung: Verlinkung eingefügt
Der Administrator hat öffentliche Schreibrechte deaktiviert.
Folgende Benutzer bedankten sich: MSC, Q-Diamond, Crash343
|
|
Finde das eine coole Idee. Ich warte gespannt auf deine weiteren Beiträge.
PS: bin auch ein Fan von Raspberry Pi |
Der Administrator hat öffentliche Schreibrechte deaktiviert.
|
|
Teil 2 – Wie alles begann
(Zurück zu Teil 1) Es war zu einer Zeit, da war Minecraft noch in der Beta. Genauer gesagt war die Beta 1.5.1 die aktuelle Version. Zu dieser Zeit hatte ich über Mitschüler in meiner damaligen Schule das Spiel kennen gelernt und war auch recht bald auf einem von einem Mitschüler gehostetem Server aktiv. Mit gefiel das Spiel, aber recht bald hatte es mich genauer interessiert, wie das Spiel eigentlich technisch umgesetzt wurde. Dazu solltet ihr Wissen, dass ich damals schon einige Programmiererfahrung hatte. Überwiegend hatte ich mich mit der Programmiersprache (und Entwicklungsumgebung) „Blitz Basic 3D“ beschäftigt. Mit dieser Sprache kannte ich mich schon bestens aus und wie der Name vermuten lässt, ist diese Sprache darauf ausgelegt 3D-Anwendungen zu entwickeln. Insbesondere ist diese für die Spielentwicklung gedacht, weshalb diverse, dafür essentielle Funktionen wie ein Grafik-Fenster zu erzeugen, einfache geometrische Formen zu zeichnen, aber auch das einlesen von Maus und Tastatureingaben sehr einfach umsetzbar sind. Wer mal ein kleines Beispiel sehen möchte, der sei auf die folgenden paar Zeilen Code verwiesen. Diese erstellen schon eine 3D Ausgabe, welche einen drehenden 3D-Würfel darstellt. Graphics3D 1024, 768, 32, 2
SetBuffer BackBuffer()
Local camera = CreateCamera()
TranslateEntity camera, 0, 0, -5
Local light = CreateLight(1, camera)
Local cube = CreateCube()
While True
TurnEntity cube, 1, 1, 1
RenderWorld()
Flip
Wend Ebenfalls bietet diese Sprache aber auch die Möglichkeit Dateien einzulesen und über das Netzwerk zu kommunizieren. Da ich mich in der Sprache schon an mehreren kleinen bis größeren 3D-Projekten versucht hatte, wollte ich einfach mal versuchen die Grundlage für ein „eigenes Minecraft“ zu schaffen. Im wesentlichen wollte ich damit mal testen, wie schwer es eigentlich ist, so eine „Blockumgebung“ umzusetzen. Ich hatte relativ schnell ein kleines Testprogramm zusammen, jedoch war auch schnell klar, dass dieser Testaufbau nicht gut mit großen Mengen an Blöcken klar kommen würden. Soll heißen: Für ein paar hundert Blöcke hätte das auf diese weise gut funktioniert, aber für solche Mengen wie sie in Minecraft üblich sind, hätte mit dieser Technik das kein Rechner vernünftig darstellen können. Ich hatte mir an der Stelle noch weitere Gedanken gemacht, auch recht bald konkrete Ideen gehabt wie es besser geht, es an dieser Stelle erst mal gut sein lassen und wieder das richtige Minecraft weiter gespielt. Nach einer Weile hatte mich jedoch etwas anderes interessiert. Es war die Frage, wie Minecraft eigentlich die Netzwerkkommunikation regelt. Ich hatte mal bisschen eine Suchmaschine mit Arbeit versorgt und war nach einer Weile auf eine inoffizielle Dokumentation des Minecraft Netzwerkprotokolls gestoßen. Recht schnell hatte ich mich grob eingelesen und mir gedanken darüber gemacht, ob es denn irgendwie sinnvoll wäre einen eigenen Client (also ein eigenes Darstellungsprogramm) zu schreiben. Einen guten Grund dafür diesen Aufwand auf mich zu nehmen hatte ich zwar nicht gefunden (außer vielleicht das Spiel ohne Java spielen zu können), aber das Interesse das mal auszuprobieren hatte ich trotzdem. An der Stelle mal einen kurzen Ausflug zu Netzwerkprotokollen. Minecraft arbeitet mit einer sogenannten TCP/IP Verbindung. Eine TCP Verbindung stellt dabei eine serielle Verbindung zwischen zwei Programmen dar. Diese Programme können an beliebigen Orten laufen, dies funktioniert also sowohl Lokal auf einem Rechner ohne echte Netzwerkverbindung als auch über das Internet. Zumindest sofern einem keine Firewall einen Strich durch die Rechnung macht. Wenn nun zwei Programme so eine TCP Verbindung hergestellt haben, können diese sich in beide Richtungen Daten schicken. Technisch gesehen handelt es sich hier um eine Bit-Übertragung, aus Sicht des Programmierers arbeiten diese Verbindungen aber auf Byte Ebene. Soll bedeuten, dass ein Programm an einem Ende einen „Datenblock“ bestehend aus einzelnen Bytes in einen Speicherpuffer kopieren kann und die TCP Verbindung übernimmt dann alles nötige um diese Daten an das Zielprogramm zu übermitteln. Dieser Schritt, die Daten in ein „Bytearray“ zu schreiben wird auch Serialisierung genannt. Das Programm am anderen Ende kann diese Daten dann wieder auslesen und verarbeiten. Ein Netzwerkprotokoll wie das von Minecraft nutzt nun so eine TCP Verbindung als Grundlage und spezifiziert selbst einzelne Datenpakete. Dabei hat Minecraft für jedes Ereignis das es im Spiel gibt ein eigenes Netzwerkpaket. Zum Beispiel wenn ein Spielobjekt im Spiel erzeugt wird, sagen wir zum Beispiel mal ein Skelett, dann wird über diese TCP Verbindung ein Paket mit dem Namen „Spawn Entity“ an den Client gesendet (der Name dient nur der Beschreibung). In diesem Paket stehen dann Dinge wie die eindeutige ID, die Weltposition und Ausrichtung sowie Metadaten (die Metadaten beinhalten alle nicht immer vorhandenen Daten, wie zum Beispiel die Rüstung die ein Skelett tragen kann). Diese Daten werden als Bytefolge über diese TCP Verbindung gesendet und vom Client empfangen. Wer sich das mal genauer anschauen möchte, der sei hier auf die erwähnte Dokumentation verwiesen (Link). Nun weiter in der Geschichte: Ich hatte also die Protokollbeschreibung gefunden und entschieden los zu legen. Ich schrieb ein kleines Programm, welches die nötigen Pakete der damaligen Minecraft Version einlesen und auch senden konnte. Dazu sollte ich sagen das Minecraft Protokoll sehr asymmetrisch ist was die Paketanzahl angeht. Soll bedeuten, dass es viel mehr unterschiedliche Pakete gibt, welche der Server einem senden kann, als jene, welche der Client senden können muss. Ich tastete mich also langsam ran und habe „on demand“ die nötigen Pakete in meinem Programmcode implementiert. Mit „on demand“ meine ich hier, dass ich erst mal geschaut hatte, welche Pakete den der Server so sendet. Und nur jene habe ich auch eingebaut. Bedeutete aber auch, dass immer wenn ein Datenpaket kam, welches ich noch nicht eingebaut hatte, dass mein Programm dann abbrechen musste, weil es die Daten nicht interpretieren konnte. Als Grundlage hatte ich oft auch einen Paketsniffer angeworfen und damit geschaut, wie die Datenpakete vom richtigen Minecraft Client aussehen. Insbesondere für die Loginsequenz, in welcher der Client die Pakete in einer bestimmten Abfolge senden muss war hier ein Problematischer Teil. Nach einer Weile hatte ich es jedoch geschafft und hatte ein kleines Programm, welches sich zu einen Minecraft Server verbinden konnte. Aber, ihr ahnt es, viel mehr konnte es auch noch nicht. Ich hatte dann noch einfache Features eingebaut, wie den Ingame Chat ausgeben und Nachrichten senden, oder die Möglichkeit Informationen über Spielobjekte in der Nähe auszugeben. Was ich jedoch nicht konnte, waren Informationen über die Umgebung richtig Interpretieren. An dieser Stelle kommen wir zu etwas, was Minecraft von den meisten anderen Spielen unterscheidet. Die meisten Spiele haben Spieldaten wie die Spiellandschaft als statische Modelle. Diese liegen dann in Dateien vor, welche in der Regel im Spielordner zu finden sind. Nicht so bei Minecraft. Hier hat nur der Server wirklich alle Daten der Spielumgebung. Die gesamte Landschaft besteht ja nur aus Blöcken, und diese Daten werden über das Netzwerk übertragen, sobald man einem Server beitritt. Dies hatte für mich den Vorteil, dass ich eigentlich keine Daten aus dem Spielordner von Minecraft lesen können musste. Mein Problem an der Stelle war jedoch, dass die Spielwelt-Daten, welcher der Server sendet, „gepackt“ waren. Diese Daten wurden vor der Übertragung mit „gzip“ komprimiert. An sich eine sehr gute Sache, ihr könnt euch das so ähnlich vorstellen wie wenn ihr Daten mit „ZIP“ oder ähnlichem packt, nur das hier ein Kompressionsniveau von meist ~3% erreicht wurde. Mein Problem an der Stelle war aber Blitz Basic. Den das konnte die Sprache von sich aus nicht. Aber ich hatte Glück und hatte eine Programmbibliothek gefunden, welche dies dann doch ermöglicht hatte und für Blitz Basic ausgelegt war. Nach einigem herum experimentieren hatte ich es dann auch geschafft diese Daten richtig einzulesen. Nun hatte mein Programm also auch Wissen über die Spielumgebung. Aber Wissen alleine reicht halt nicht aus, den wirklich viel machen konnte ich damit immer noch nicht. Nach einer weiteren etwas längeren Pause und nicht so richtig die Idee wie ich das nun weiter bauen sollte, kam mir während den beginn eines größeren Bauprojektes auf dem Minecraft Server eine Idee. Anstatt einen komplett eigenen Client zu schreiben, könnte ich stattdessen doch ein Programm schreiben, welches sich nur zwischen den richtigen Client und den Server schaltet. Der Sinn dahinter war es, nicht einen weiteren Spieler zu haben welcher nur „doof herumsteht“, sondern das einfach ich der Spieler bin und durch dieses Zwischenprogramm zusätzliche Informationen erhalten kann. Dabei waren die ersten Ideen so etwas wie ein Frühwarnsystem wenn sich ein Creeper nähert. Aber auch die Info wo man die gesuchten Rohstoffe findet wären im Bereich des Umsetzbaren gewesen. Gut, ging es also wieder ans programmieren. Jetzt schrieb ich eben eine Anwendung welche 2 TCP Verbindungen öffnen würde. Eine, welche mit dem Client verbunden wird und die zweite, welche sich zu dem Server verbindet. Alle Daten welche in eine Verbindung eingehen würden, sollten direkt an die andere weiter gegeben werden. Jedoch sollte zeitgleich mein Programm diese Daten auch interpretieren. Das ganze kennt man auch unter dem Begriff „Man in the Middle“, wobei fürs erste die Daten nur weiter geleitet und nicht verändert werden sollten. Für dieses Vorhaben konnte ich einiges aus dem Programmcode, welchen ich ja bereits vorher für den eigenen Client geschrieben hatte wieder verwenden. Ich musste nur auf ein paar Details achten, weil inzwischen die Beta 1.7 von Minecraft erschienen war. In dieser wurde das Netzwerkprotokoll leicht angepasst. Also ein paar Pakete hatten sich von ihrem Aufbau verändert und es kamen neue hinzu. Innerhalb von ein paar Tagen hatte ich es geschafft die Grundlage für diese Funktionalität umzusetzen. Jedoch hatte ich hin und wieder mit Paketen zu kämpfen, welche vorher nicht übertragen wurden weil das entsprechende Ereignis im Spiel noch nicht aufgetreten war. Die Idee mit dem Frühwarnsystem hatte ich jedoch nie umgesetzt, stattdessen hatte ich eine interessantere Idee. Ich hatte mich noch weiter mit der Übertragung der Weltdaten beschäftigt und hatte damit experimentiert diese Daten zu „manipulieren“. Nach einigem basteln war ich dann in der Lage, die Daten, welche der Client vom Server erhält, auszutauschen und andere Blöcke einzufügen. In den ersten Versuchen hatte ich so nen spaß gemacht wie alles Wasser durch Lava ersetzen, oder die Luft durch Wasser. Ich denke die Screenshots sprechen da für sich selbst . Ein wenig später hatte ich dann aber auch einen sinnvolleren Einsatz gefunden. Und zwar um größere Bauwerke als „Hilfsschablone“ zum aufbauen einzufügen. Ich hatte es mir damit zum Projekt gemacht, dass Raumschiffsmodell der „Enterprise E“ aus Star Trek nach zu bauen. Ich hatte mir dafür ein 3D-Mesh (Drahtgittermodell) genommen und in die entsprechende Blockform umgerechnet. Danach dieses mit meinem Programm so verknüpft, dass es immer eine Höhenschicht des Modells in die Minecraft Welt an die Zielposition einfügt, wobei die Blöcke extra um eines nach unten verschoben waren und aus einem auffälligen Material bestanden. Auf diese weise hatte ich effektiv im Spiel eine Vorlage, aus welcher man leicht erkennen konnte, wo die Blöcke der aktuellen Bauschicht platziert werden müssen. Als „auffälliges Material“ hatte ich dafür Bedrock (Grundgestein) und Glas gewählt. Wie ihr euch sicher vorstellen könnt, erleichtert dies den genauen Aufbau des Zielmodells ungemein. Ich musste nur darauf aufpassen, dass diese eingefügten Blöcke zwar für den Client existierten, für den Server (und andere Spieler) aber natürlich nicht, da diese ja nur von meinem Zwischenprogramm eingefügt wurden. Daher durfte ich auf diesen auch nicht herum laufen, da mich der Server sonst umgehend vom Server gekickt hatte, mit der Begründung das ich fliegen würde. Ebenfalls konnte man beim bauen die Blöcke nicht direkt an die „Schablone“ dran setzen, sondern man kann Blöcke nur an bereits existierende Blöcke ansetzen. Der Versuch führte zwar nicht zu einem Kick vom Server, hatte aber zur Folge das der Server dem Client nochmal die betroffenen Blöcke neu sendet, womit ein Teil der Schablone „überschrieben“ wurde. Alles in allem hat dies jedoch schon recht gut funktioniert und ich konnte damit die Grundform des Modells komplett (im Survival) aufbauen. Nach einer Weile war es jedoch wieder so weit und eine neue Minecraft Version war erschienen. Diesmal war es die Beta 1.9. Da diese ebenfalls wieder einige Datenpakete veränderte, hatte ich bisschen die Lust verloren weiter an dem Programmcode herum zu basteln. Das ganze wurde langsam immer unübersichtlicher und die richtigen Stellen zu finden, wo es durch geänderte Pakete zu Problemen gekommen war, wurde recht nervig. Nach einer Weile herum basteln hatte ich dann genug und dieses Programm erst mal so gelassen. Ich glaube ich hatte es Teilweise wieder kompatibel bekommen, jedoch hatte ich es effektiv nicht mehr eingesetzt. So, soviel zu diesem Teil. Ich hoffe der Text ist einigermaßen verständlich geschrieben. Wenn nicht dürft ihr natürlich gerne Rückfragen stellen, bevor es im nächsten Teil erzählte, wie es weiter ging mit dem Projekt. |
Letzte Änderung: 07 Okt 2016 12:03 von Tirus.
Der Administrator hat öffentliche Schreibrechte deaktiviert.
|
Keine Termine |
Dieser Clan verfügt über einen eigenen Minecraft Server!
Einfach euren Minecraft Namen mit in das Profilfeld eingeben und ihr könnt den Server betreten!
(Falls es Probleme damit gibt, einfach in der Box melden!)
Last Update: 25.05.2015
Teamspeak 3 Serveradresse wie Websiteadresse |
|
Minecraft v. 1.17.1 (Amplified Modus) |