Socket - Auf einen Minecraftserver als Spieler verbinden

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
Benutzeravatar
jtschoch
User
Beiträge: 400
Registriert: Freitag 6. Mai 2011, 15:40
Kontaktdaten:

Hallo,

Ich wollte eigentlich ein Programm schreiben mit den ich mich auf einen Minecraftserver als Spieler verbinden und Nachrichten/Befehle senden kann.
Ich habe zwar im Internet schon Beispiele gesehen und Probiert nur es klappt nicht und es kommt das Verbindung verloren hat.
http://stackoverflow.com/questions/9520 ... -in-python

Wie könnte ich das realisieren ich? Ich kapier das mit dem Pakets nicht ganz.

Wie sende ich Packets mit Daten wie Nachrichten und so?
http://wiki.vg/Protocol
Meine Webseite http://www.develos.de
Forum: http://www.develos.de/forum
Mein Minecraft-Server: jonel.minecraft.to [dynmap(:8123)] | Webseite: http://jonel-minecraft.tk
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Das was in der StackOverflow-Diskussion zu sehen ist, ist halt Minecraft Classic.

Letztendlich verbindest du dich als Client mit der Socket Library. Mit dieser kannst du Strings in bestimmtem Format senden und empfangen. Das Format wird ja in der von dir verlinkten Webseite erklärt. Zudem gibt es das mitgelieferte Modul `struct` mit dem du solche Pakete bauen kannst, sowie die externe Library `bitstring` mit der das etwas einfacher geht.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Armageddon
User
Beiträge: 7
Registriert: Dienstag 16. März 2010, 15:09
Wohnort: Ruhla

Hallo!

Soetwas habe ich schoneinmal gemacht, aber vor einiger Zeit (naja, paar Monate, aber bei der rasanten Entwicklung).
Zuerst etwas Allgemeines: Das Minecraft-Protokoll ist ganz und garnicht benutzerfreundlich. Wenn ich vorhätte, so ein Protokoll zu entwerfen, sähe dass so aus:

1Byte: Packettyp/id 4Byte: Packelänge 4Byte: Irgendeine Prüfsumme

Es ist aber längst nicht so! Das Minecraftprotokoll kenn nur den ersten Wert, den Packettyp. Die Länge musst du selbst berechnen können. Das ist bei vielen vorgegeben, bei anderen ist es doch irgendwo in dem Packet gespiechert. Auch eine Prüfsumme gibt es nicht. Da höhrt sich harmlos an, aber - wenn ein Packet kommt, welches du nicht verstehst, das du fast keine Chanche, dich wieder in den Datenstrom einzuklinken. Du weist ja nicht, wie groß das letzte Packet war, kannst also auch den Anfang des nächsten nicht finden, um dessen Größe zu berechnen. Auch die Idee, ich warte, bis einen Moment nichts aus der Leitung kommt und dann muss ja das nächste Byte der Anfang eines Packet sein, funktioniert in der Praxis nicht, das ein schon ein Pixelhuhn bei jedem Schritt nen Packet erzeugt.

Hier ist ein Zip-Packet: http://ubuntuone.com/40UEwctc5W33LmLsCHKCbu
  • bot.py sollte ein Farmbot werden
  • proto.py die Protokolldefinitionen. Die hatte ich irgendwo her, weis aber nicht mehr woher!
  • ucs2 und construct Bibliotheken, die waren bei proto.py dabei, vlt. ist das ein Anhaltspunkt für sich, die Quelle zu suchen. Ich weis aber nicht, ob die noch weiterentwickelt worden ist
Der inhalt wird auf einem aktuellen Server nicht funktionieren, du musst also entweder die Packetdefinitionen von Hand aktualisieren, oder die Quelle für proto.py suchen, und die dort eine neuere Version suchen.

Woher die informationen, wie die Pakete aufgebaut sind (Weiter ober ist aktueller aber auch mehr Aufwand):
  • Minecraft dekompilieren (gibt Anleitungen und Packete im Netzt)
  • http://www.wiki.vg/Protocol (in english)
  • Die Quelle von proto.py, die war auch damals nicht wirklich aktuell
Ich werd auch mal nach der Quelle von proto.py suchen, Tschüss
Benutzeravatar
jtschoch
User
Beiträge: 400
Registriert: Freitag 6. Mai 2011, 15:40
Kontaktdaten:

Vielen Dank, ich werde mir das Ganze mal ansehen.
Ich weis selbst das das Minecraftprotokoll eine Katastrophe ist.
Letztendlich verbindest du dich als Client mit der Socket Library. Mit dieser kannst du Strings in bestimmtem Format senden und empfangen.
Dies hatte ich schon selbst rausgefunden und wusste wie das geht.
Meine Webseite http://www.develos.de
Forum: http://www.develos.de/forum
Mein Minecraft-Server: jonel.minecraft.to [dynmap(:8123)] | Webseite: http://jonel-minecraft.tk
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

jtschoch hat geschrieben:Dies hatte ich schon selbst rausgefunden und wusste wie das geht.
Ok, dann was ist jetzt genau deine Frage?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jtschoch
User
Beiträge: 400
Registriert: Freitag 6. Mai 2011, 15:40
Kontaktdaten:

Na wie ich genau die Sockets in die Form bringe das sie der Server annimmt, denn ich versehe noch nicht ganz wie ich z.B. eine Chatnachricht sende.
Was ich verstanden habe ist das System mit den Socket, oder besser gesagt das Protokoll das der Server halt die bestimmten Bytes annimmt und mir eventuell eine Antwort zurücksendet.

Mein Eigentliches Ziel ist es das ich die gerade Online befindenden Spieler namen abfange und aufzulisten, ist dies Möglich ohne erst über das Protokoll sich als Spieler einzuloggen?

Hier hatte ich mal einen Abschnitt aus dem Internet um einen Server anzupingen, daraus hatte ich das Script noch mit dem Modul curses erweitert, eine kleine Anzeige erstellen lassen.

Bissel doof Formuliert egal.

Hier der Code:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

import curses
import socket
import struct, time, sys


 #http://wiki.vg/Pocket_Minecraft_Protocol
def get_info(host='128.65.218.229', port=25565):
    try:
        #Set up our socket
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((host, port))
        
        #Send 0xFE: Server list ping
        s.send('\xfe\x01')
     
        #Read some data
        d = s.recv(1024)
        s.close()
        
        #Check we've got a 0xFF Disconnect
        assert d[0] == '\xff'
        
        #Remove the packet ident (0xFF) and the short containing the length of the string
        #Decode UCS-2 string
        d = d[3:].decode('utf-16be')
        
        #Check the first 3 characters of the string are what we expect
        assert d[:3] == u'\xa7\x31\x00'
        
        #Split
        d = d[3:].split('\x00')
        
        #Return a dict of values
    except: 
        d = [u'Konnte Protokollversion nicht ermitteln!', u'Konnte Serverversion nicht ermitteln!', u'Nicht Vorhanden                  ', u'0', u'0']
    return {'protocol_version': (d[0]),
            'server_version':       d[1],
            'motd':                 d[2],
            'players':          (d[3]),
            'max_players':      (d[4])}


def make_window(name, players, max_players, protocol_version, server_version):
    stdscr.addstr(2, 3, "Serverstatus                                                           ")
    stdscr.addstr(3, 3, "========================================                               ") 
    stdscr.addstr(5, 4, "Servername: {0}                                                        ".format(name))
    stdscr.addstr(6, 4, "========================================                               ")
    stdscr.addstr(7, 4, "Aktuell Online: [{0} / {1}]                                            ".format(players, max_players))
    stdscr.addstr(8, 4, "Minecraft Server Version: Minecraft {0}                                ".format(server_version))
    stdscr.addstr(10, 4, "Protokoll Version: {0}                                                ".format(protocol_version))
    stdscr.refresh()

if __name__ == "__main__":
    stdscr = curses.initscr()
    curses.noecho()
    curses.cbreak()

    try:
        while(1):
            info = get_info()
            make_window(info["motd"], info["players"], info["max_players"], info["protocol_version"], info["server_version"])
            time.sleep(5)
    finally:
        curses.echo()
        curses.nocbreak()
        curses.endwin()
Meine Webseite http://www.develos.de
Forum: http://www.develos.de/forum
Mein Minecraft-Server: jonel.minecraft.to [dynmap(:8123)] | Webseite: http://jonel-minecraft.tk
Armageddon
User
Beiträge: 7
Registriert: Dienstag 16. März 2010, 15:09
Wohnort: Ruhla

Naja, ein Anfang wäre vielleicht ein "Spam-bot" - den bekommst du ohne Auswertung der vom Server gesendeten Daten hin.
  1. Verbinden
  2. Packet erzeugen: Handshake (0x02)
  3. Ein paar Sekunden warten
  4. Packet erzeugen: Client Statuses (0xCD)
Danach solltest du duch mit dem echten Client verbinden und deinen Bot sehen. Also nächstes kann dieser Chat Message (0x03) Pakete erzeugen. Noch ein Tipp: sammle alle Daten vom Server in einem String. Dann kannst du, wenn die Verbingung vom Server abgebrochen worden ist mit alledaten.split("\xff")[-1][2:].decode("UCS-2") dem Grund ausgeben. Soll dein Bot/Client natürlich wirklich in der Welt umherlaufen, musst du die Packete des Servers richtig verstehen können.

(EDIT: Die Längenangabe der Zeichenkette ist ein Short, du musst also 2 Bytes überspringen und natürlich auch das Encoding beachten)
Zuletzt geändert von Armageddon am Dienstag 19. Februar 2013, 17:44, insgesamt 1-mal geändert.
BlackJack

@Armageddon: Aber war nicht gerade das Problem von diesem Protokoll, dass man alle Pakete verstehen und verarbeiten *muss*, zumindest so weit, dass man die Paketgrösse kennt. Denn sonst weiss man ja gar nicht wo die Paketgrenzen sind.
Armageddon
User
Beiträge: 7
Registriert: Dienstag 16. März 2010, 15:09
Wohnort: Ruhla

Naja, war halt nur ein QaD-Vorschlag. Es gibt nun mal einen bestimmten Protokollablauf und man kann davon ausgehen, dass der Server ihn einhält. Also einfach seine Packete senden, dabei den Server zwischendurch Zeit lassen, seine Daten zu senden, die man aber garnicht beachtet!
Die Lösung, um den Kick-Grund herrauszufinden basirt darauf, dass das letzte Packet der Übertragung, wenn nicht vom Client abgebrochen, mit 99% Wahrscheinlichkeit das Packet 0xFF ist. In einem normalen String kommt kommt dieses Byte nicht vor. Also sucht man von hinten her das letzte \xFF, geht noch zwei Zeichen, welche ja die Länge des nun folgenden Strings kodieren, weiter und hat dann den Grund. Natürlich kann die Verbindung auch ungeplant beendet werden, für diesen Fall sollte man schauen, ob der len(Grund) > 240, denn laut http://www.wiki.vg/Data_Types kann die Zeichenkette nicht länger sein.
Antworten