Schei� encoding

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Py-Prog
User
Beiträge: 673
Registriert: Dienstag 16. Februar 2010, 17:52
Wohnort: G:\ermany

Ich hab jetzt mal angefangen mir meinen eigenen praser zu basteln:

Code: Alles auswählen

def prase_mc_packet(socket_obj, to_):
    global packet_conntent
    packet_conntent = bytes()
    def socket_read(socket_obj, len_):
        global packet_conntent
        recv_packet = bytes()
        while len_ != len(recv_packet):
            recv_packet += socket_obj.recv(len_-len(recv_packet))
        packet_conntent += recv_packet
        return recv_packet
    prased_packet_conntent = []
    packet_type = socket_read(socket_obj, 1)
    print('======', packet_type)
    if type(packet_structs.structs[packet_type]) == tuple:
        packet_struct = packet_structs.structs[packet_type]
    else:
        packet_struct = packet_structs.structs[packet_type][to_]

    print(packet_struct)
    
    for var_type in packet_struct:
        if type(var_type) == tuple:
            var_type = var_type[0]
            
        if var_type == 'string16':
            len_ = bitstring.BitStream(socket_read(socket_obj, 2)).read('uint:16')*2
            prased_packet_conntent.append(bitstring.BitStream(socket_read(socket_obj, len_)).read('bytes:' + str(len_)).decode('utf-16be'))
        elif var_type in ['bytes', 'bool']:
            prased_packet_conntent.append(bitstring.BitStream(socket_read(socket_obj, 1)).read(var_type))
        elif var_type.split(':')[0] in ['int', 'float']:
            bitstream = bitstring.BitStream(socket_read(socket_obj, int(int(var_type.split(':')[1])//8)))
            prased_packet_conntent.append(bitstream.read(var_type))
        else:
            print('UNKNOWN type:', var_type)
    return [prased_packet_conntent, packet_conntent]
(man übergibt ein Socket Objekt und einen String: entweder 'CLIENT_TO_SERVER' oder 'SERVER_TO_CLIENT'. packet_structs ist FAST gleich mit dem von den Link von Dav1d, ich habs noch etwas um geändert.)


So und wer sich mal den Code unten ansieht wird merken das bis zum Fehler alles einwandfrei funktioniert ...

Code: Alles auswählen

====== b'\xfe'
()
CLIENT: [[], b'\xfe']
====== b'\xff'
('string16', 'reason')
UNKNOW type: reason
SERVER: [['/register wunschpw wunschpw§2§14'], b'\xff\x00 \x00/\x00r\x00e\x00g\x00i\x00s\x00t\x00e\x00r\x00 \x00w\x00u\x00n\x00s\x00c\x00h\x00p\x00w\x00 \x00w\x00u\x00n\x00s\x00c\x00h\x00p\x00w\x00\xa7\x002\x00\xa7\x001\x004']
('13.37.13.37', 1337) connected to server!
====== b'\x02'
('string16', 'username_host')
UNKNOW type: username_host
CLIENT: [['1337;13.37.13.37:25565'], b'\x02\x00\x16\x001\x003\x003\x007\x00;\x002\x00.\x002\x002\x00.\x001\x001\x00.\x001\x003\x007\x00:\x002\x005\x005\x006\x005']
====== b'\x02'
('string16', 'connection_hash')
UNKNOW type: connection_hash
SERVER: [['-'], b'\x02\x00\x01\x00-']
====== b'\x01'
(('int:32', 'protocol_version'), ('string16', 'username'), ('string16', 'not_used_1'), ('int:32', 'not_used_2'), ('int:32', 'not_used_3'), ('bytes', 'not_used_4'), ('bytes', 'not_used_5'), ('bytes', 'not_used_6'))
CLIENT: [[29, '1337', '', 0, 0, b'\x00', b'\x00', b'\x00'], b'\x01\x00\x00\x00\x1d\x00\x04\x001\x003\x003\x007\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00']
====== b'\x01'
(('int:32', 'entity_id'), ('string16', 'not_used_1'), ('string16', 'level_type'), ('int:32', 'game_mode'), ('int:32', 'dimension'), ('bytes', 'not_used_2'), ('bytes', 'world_height'), ('bytes', 'max_players'))
SERVER: [[94533, '', 'default', 0, 0, b'\x01', b'\x00', b'\x0e'], b'\x01\x00\x01qE\x00\x00\x00\x07\x00d\x00e\x00f\x00a\x00u\x00l\x00t\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x0e']
====== b'\r'
(('float:64', 'x'), ('float:64', 'y'), ('float:64', 'stance'), ('float:64', 'z'), ('float:32', 'yaw'), ('float:32', 'pitch'), ('bool', 'on_ground'))
CLIENT: [[8.5, 65.0, 66.62000000476837, 8.5, -180.0, 0.0, False], b'\r@!\x00\x00\x00\x00\x00\x00@P@\x00\x00\x00\x00\x00@P\xa7\xae\x14\x80\x00\x00@!\x00\x00\x00\x00\x00\x00\xc34\x00\x00\x00\x00\x00\x00\x00']
====== b'\xfa'
(('string16', 'channel'), ('int:16', 'data_size'))
SERVER: [['REGISTER', 6], b'\xfa\x00\x08\x00R\x00E\x00G\x00I\x00S\x00T\x00E\x00R\x00\x06']
====== b'\x0b'
(('float:64', 'x'), ('float:64', 'y'), ('float:64', 'stance'), ('float:64', 'z'), ('bool', 'on_ground'))
CLIENT: [[8.5, 64.92159999847412, 66.5416000032425, 8.5, False], b'\x0b@!\x00\x00\x00\x00\x00\x00@P:\xfb~\x8f\\)@P\xa2\xa9\x93\x0f\\)@!\x00\x00\x00\x00\x00\x00\x00']
====== b'W'
----------------------------------------
Exception happened during processing of request from ('13.37.13.37', 1337)
Traceback (most recent call last):
  File "C:\Python31\lib\socketserver.py", line 558, in process_request_thread
    self.finish_request(request, client_address)
  File "C:\Python31\lib\socketserver.py", line 320, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "C:\Python31\lib\socketserver.py", line 614, in __init__
    self.handle()
  File , line 112, in handle
    self.recv_server = prase_mc_packet(self.server_connect_socket, 'SERVER_TO_CLIENT')
  File , line 25, in prase_mc_packet
    if type(packet_structs.structs[packet_type]) == tuple:
KeyError: b'W'
----------------------------------------
Tja, den Key b'W' gibt es nicht, auch nicht b'\x57', aber selbst wenn würde das egal sein weil das gleich ist.
In der Doku steht auch nichts von b'W' bzw. b'\x57' drinnen, also geht ich davon aus das in meinen praser ein Fehler ist, und sich dadurch irgendwas verschoben hat und dadruch das b'W' zum ersten Paketteil wird. Ich hab aber keine Ahnung wo der Fehler ist, weil ja der Rest funktioniert, einen Fehler vom Server kann ich eigentlich auch ausschließen, weil ich ja sonst auch drauf spielen kann.
Technik ist: wenn alles funktioniert und keiner weiß warum.
Wer Rechtschreibfehler findet darf sie behalten.
BlackJack

@Py-Prog: Also so wie Du `bitstring` benutzt, hättest Du auch bei `struct` oder `ctypes` aus der Standardbibliothek bleiben können.

In Deiner Ausgabe steht ein paar mal das ein unbekannter Typ vorkam. Unter anderem für 'string16'. Und damit dürfte der gezeigte Quelltext nicht zur gezeigten Ausgabe passen.

Am gruseligsten finde ich an dem Quelltext ``global`` und `prased_packet_conntent`. Das heisst „Parser” und „parsed” und „content”.
Py-Prog
User
Beiträge: 673
Registriert: Dienstag 16. Februar 2010, 17:52
Wohnort: G:\ermany

BlackJack hat geschrieben:In Deiner Ausgabe steht ein paar mal das ein unbekannter Typ vorkam. Unter anderem für 'string16'. Und damit dürfte der gezeigte Quelltext nicht zur gezeigten Ausgabe passen.
WAS? wo steht da das string16 unbekannt ist??? Das was da immer steht ('string16', 'beschreibung') ist nur dazu da das ich sehe was das Programm macht, wie die ganzen anderen print anweisungen auch.
BlackJack hat geschrieben:Am gruseligsten finde ich an dem Quelltext ``global`` und `prased_packet_conntent`. Das heisst „Parser” und „parsed” und „content”.
Ohne global geht das aber nicht, und ich frage mich allmählich echt warum es dieses "gruselige" global überhaupt gibt wenn man es nicht verwenden darf???
Die Rechtschreibfehler werde ich natürlich sofort ausbessern :oops: , ich lese oft etwas so wie es sich für mich besser/schöner/... klingt ohne das aber bewusst zu tun ... :oops:
Technik ist: wenn alles funktioniert und keiner weiß warum.
Wer Rechtschreibfehler findet darf sie behalten.
BlackJack

@Py-Prog: Und manchmal steht nach ``('string16', 'beschreibung')`` der Text ``UNKNOW type: beschreibung``. Beispiel:

Code: Alles auswählen

====== b'\xff'
('string16', 'reason')
UNKNOW type: reason
Natürlich geht das ohne ``global``. Warum es ``global`` gibt, frage ich mich auch manchmal. Die paar wenigen Male wo das tatsächlich nützlich ist, käme man auch durch Importieren des Moduls an das Modul heran. Das ist etwas umständlich und nicht so schön, aber extrem unschön, weil unsauber, ist ``global`` ja auch.

Man könnte eine Klasse schreiben, welche die Leseoperationen kapselt. Und zum Beispiel auch gleich Methoden bietet um die verschiedenen Typen, die in den Paketen vorkommen können, auszulesen.

Der Fehler ist im Parsen der '\xfa'-Pakete. Der Wert 'data_size' gibt an wieviele Bytes danach *noch zum Paket gehören*! Die muss man dann natürlich auch noch *lesen*, damit man das Paket vollständig verarbeitet hat.
Py-Prog
User
Beiträge: 673
Registriert: Dienstag 16. Februar 2010, 17:52
Wohnort: G:\ermany

BlackJack hat geschrieben:@Py-Prog: Und manchmal steht nach ``('string16', 'beschreibung')`` der Text ``UNKNOW type: beschreibung``. Beispiel:

Code: Alles auswählen

====== b'\xff'
('string16', 'reason')
UNKNOW type: reason
Ja und? reason ist unbekannt, aber nicht string16, wenn du nämlich die Zeilen drunter anschaust sollte dir eigentlich auffallen das da ein string in der Liste ist ...
BlackJack hat geschrieben: Natürlich geht das ohne ``global``. Warum es ``global`` gibt, frage ich mich auch manchmal. Die paar wenigen Male wo das tatsächlich nützlich ist, käme man auch durch Importieren des Moduls an das Modul heran. Das ist etwas umständlich und nicht so schön, aber extrem unschön, weil unsauber, ist ``global`` ja auch.

Man könnte eine Klasse schreiben, welche die Leseoperationen kapselt. Und zum Beispiel auch gleich Methoden bietet um die verschiedenen Typen, die in den Paketen vorkommen können, auszulesen.
Dann mach ich halt aus der Funktion eine Klasse ...
BlackJack hat geschrieben:Der Fehler ist im Parsen der '\xfa'-Pakete. Der Wert 'data_size' gibt an wieviele Bytes danach *noch zum Paket gehören*! Die muss man dann natürlich auch noch *lesen*, damit man das Paket vollständig verarbeitet hat.
Oh stimmt, hätte ich eigentlich schon daran merken müssen das ich auf den Server nicht Fliegen kann, aber der "on_ground" wert auf False ist ... und die anderen Koordinaten können auch nicht stimmen, auch wenn sie auf den ersten blick so aussehen. Dann hoffe ich mal das ich jetzt schnell einen weg finde das Array zu parsen.
Technik ist: wenn alles funktioniert und keiner weiß warum.
Wer Rechtschreibfehler findet darf sie behalten.
Py-Prog
User
Beiträge: 673
Registriert: Dienstag 16. Februar 2010, 17:52
Wohnort: G:\ermany

Jetzt hab ich eine Klasse draus gemacht, und jetzt spinnt python mit den modulen rum, erst will es ein modul in einen unterordner nicht mehr finden trotz __init__.py geht blos mit sys.path.append('modules'). aber jetzt kommt immer noch das:
AttributeError: 'module' object has no attribute 'BitStream'
Aber bitstring hat doch eine Klasse BitStream und ich hab mal help(bitstring) ausgeben lassen und da steht auch eine klasse BitStream drinnen, und der Code ging vorher auch noch, wieso kommt da jetzt diese Komische Fehler?
Technik ist: wenn alles funktioniert und keiner weiß warum.
Wer Rechtschreibfehler findet darf sie behalten.
BlackJack

@Py-Prog: Solange Du nicht zeigst was Du genau machst und die genaue Fehlermeldung, kann man da nicht viel zu sagen. Lass Dir direkt nach dem Import doch mal das Modul per ``print`` ausgeben. Wenn ich mal wild raten sollte hast Du einem Modul den Namen `bitstring` verpasst. Oder hast zumindest ein anderes als das eigentliche im Pfad durch Deine `sys.path`-Aktion.
Py-Prog
User
Beiträge: 673
Registriert: Dienstag 16. Februar 2010, 17:52
Wohnort: G:\ermany

Der Code:

Code: Alles auswählen

import bitstring
import packet_structs

class minecraft_parser(object):
    def __init__(self, socket_obj, from_to):
        self.socket_obj = socket_obj
        self.from_to = from_to
        self.reset_vars()

    def reset_vars(self):
        self.packet_content = bytes()
        self.parsed_packet_content = []

    def __read(self, len_):
        recv_packet = bytes()
        while len_ != len(recv_packet):
            recv_packet += self.socket_obj.recv(len_-len(recv_packet))
        self.packet_content += recv_packet
        return recv_packet

    def __parse_string(self):
        len_ = bitstring.BitStream(self.__read(2)).read('uint:16')*2
        return bitstring.BitStream(self.__read(len_)).read('bytes:' + str(len_)).decode('utf-16be')

    def __parse_byte_or_bool(self, var_type):
        return bitstring.BitStream(self.__read(1)).read(var_type)

    def __parse_int_float(self, var_type):
        return bitstring.BitStream(self.__read(int(int(var_type.split(':')[1])//8))).read(var_type)

    def __parse_metadata(self):
        metadata = {}
        x = self.__read(1)
        while x != 127:
            index = x & 0x1F # Lower 5 bits
            ty    = x >> 5   # Upper 3 bits
            if ty == 0: val = self.__read(1) 
            if ty == 1: val = self.__parse_int_float('int:16') 
            if ty == 2: val = self.__parse_int_float('int:32') 
            if ty == 3: val = self.__parse_int_float('float:32') 
            if ty == 4: val = self.__parse_string()
            if ty == 5:
                val = {}
                val["id"]     = self.__parse_int_float('int:16')
                val["count"]  = self.__read(1)
                val["damage"] = self.__parse_int_float('int:16')
            if ty == 6:
                val = []
                for i in range(3):
                    val.append(self.__parse_int_float('int:32'))
            metadata[index] = (ty, val)
            x = self.__read(1)
        return metadata

    def parse_packet(self):
        self.reset_vars()
        packet_type = self.__read(1)
        print('======', packet_type)
        if type(packet_structs.structs[packet_type]) == tuple:
            packet_struct = packet_structs.structs[packet_type]
        else:
            packet_struct = packet_structs.structs[packet_type][self.from_to]

        print(packet_struct)
        
        for var_type in packet_struct:
            if type(var_type) == tuple:
                var_type = var_type[0]

            if var_type == 'string16':
                self.parsed_packet_content.append(self.__parse_string())
            elif var_type in ['bytes', 'bool']:
                self.parsed_packet_content.append(self.__parse_byte_or_bool(var_type))
            elif var_type.split(':')[0] in ['int', 'float']:
                self.parsed_packet_content.append(self.__parse_int_float(var_type))
            elif var_type == 'metadata':
                self.parsed_packet_content.append(self.__parse_metadata())
            else:
                print('UNKNOWN type:', var_type)
        return [self.parsed_packet_content, self.packet_content]
Technik ist: wenn alles funktioniert und keiner weiß warum.
Wer Rechtschreibfehler findet darf sie behalten.
BlackJack

Und wie sieht die Ausgabe von ``print bitstring`` nach dem ``import`` aus? Und wie die Fehlermeldung?
Py-Prog
User
Beiträge: 673
Registriert: Dienstag 16. Februar 2010, 17:52
Wohnort: G:\ermany

Ich wollte gard die Print ausgabe und den Traceback schicken da ist mir aufgefallen das die printausgabe das ausgiebt: \bitstring\__init__.py und die __init__.py ist ja auch leer ...
Technik ist: wenn alles funktioniert und keiner weiß warum.
Wer Rechtschreibfehler findet darf sie behalten.
Py-Prog
User
Beiträge: 673
Registriert: Dienstag 16. Februar 2010, 17:52
Wohnort: G:\ermany

So fertig, jetzt müsste ich nur noch in der packet_structs.py noch ein paar packete anpassen ...
aber dann hab ich die Wahl zwischen:
1) ein "schön" geschriebenes, nicht funktionsfähiges Programm,
2) ein meist zuverlässig (ich höffe das ich mich nicht verschätzt hab ...) funktionierendes, unschön programmiertes Programm

Ich hätte dann noch eine Möglichkeit drei, ich suche mir alle packte raus die immer gleich lang sind und überspringe die, und überspringe die packetstellen mit strings, metadaten ect. ... aber das ist auch wieder blos Arbeit mit höchst warscheinlich keinen nutzen ...
Technik ist: wenn alles funktioniert und keiner weiß warum.
Wer Rechtschreibfehler findet darf sie behalten.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Py-Prog hat geschrieben:1) ein "schön" geschriebenes, nicht funktionsfähiges Programm,
2) ein meist zuverlässig (ich höffe das ich mich nicht verschätzt hab ...) funktionierendes, unschön programmiertes Programm
Wieso schließen sich diese Fälle aus? Ich sehe nicht warum es nicht möglich sein sollte ein schön geschriebenes und funktionsfähiges Programm zu schreiben. Das das nicht klappt ist höchstens Faulheit oder Unfähigkeit.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Das ein User mit 613 Posts eine solche Frage stellt … :evil:
the more they change the more they stay the same
deets

Ich sehe nur zwei haessliche, nicht funktionierende Programme. Oder hast du schon vergessen, dass dein "gutes" Programm bei deinem Freund nicht funktioniert? Das muesste es aber - wenn es "funktionieren" soll...
Py-Prog
User
Beiträge: 673
Registriert: Dienstag 16. Februar 2010, 17:52
Wohnort: G:\ermany

Leonidas hat geschrieben:Wieso schließen sich diese Fälle aus? Ich sehe nicht warum es nicht möglich sein sollte ein schön geschriebenes und funktionsfähiges Programm zu schreiben. Das das nicht klappt ist höchstens Faulheit oder Unfähigkeit.
oder es ist einfach nicht schnell genug ...
Dav1d hat geschrieben:Das ein User mit 613 Posts eine solche Frage stellt … :evil:
was hat das mit den Beiträgen zu tun? Nur weil sich hier einige anmelden und schon seit Jahren Programmieren und schon mit wenigen Beiträgen viele Probleme gelöst haben heißt das doch lange nicht das ich dumm bin :cry:
ach ja und mittlerweile 614 Beiträge
deets hat geschrieben:Ich sehe nur zwei haessliche
danke ... :cry:
deets hat geschrieben:nicht funktionierende Programme. Oder hast du schon vergessen, dass dein "gutes" Programm bei deinem Freund nicht funktioniert? Das muesste es aber - wenn es "funktionieren" soll...
Das lag aber nur daran das der ein sehr lames inet hat, ich hab schon kein schnelles und ich kann schneller uploaden als er downlaoden kann ... ob da jetzt ein Bug drinnen ist und manchmal nicht geht oder ob es deswegen nicht (ganz) funktioniert bleibt sich doch gleich.
Technik ist: wenn alles funktioniert und keiner weiß warum.
Wer Rechtschreibfehler findet darf sie behalten.
BlackJack

@Py-Prog: Wenn ein Programm nicht mit allen Eingaben klar kommt, die der Protokollspezifikation oder der verwendeten API entsprechen, dann ist da nicht ein lahmes Internet dran Schuld, sondern das fehlerhafte Programm.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Py-Prog hat geschrieben:
Dav1d hat geschrieben:Das ein User mit 613 Posts eine solche Frage stellt … :evil:
was hat das mit den Beiträgen zu tun? Nur weil sich hier einige anmelden und schon seit Jahren Programmieren und schon mit wenigen Beiträgen viele Probleme gelöst haben heißt das doch lange nicht das ich dumm bin :cry:
So meinte ich das nicht, ich wollte dir keineswegs Dummheit unterstellen, denn dann wäre ich auch ziemlich oft dumm. Mich verwundert es nur, dass du so lange hier schon dabei bist und du noch diese Frage stellst, selbst wenn du ernsthaft darüber nachdenkst, dein "unschönes" Programm zu verwenden, solltest du ja unsere Antwort dazu kennen.
the more they change the more they stay the same
deets

Py-Prog hat geschrieben:Das lag aber nur daran das der ein sehr lames inet hat, ich hab schon kein schnelles und ich kann schneller uploaden als er downlaoden kann ... ob da jetzt ein Bug drinnen ist und manchmal nicht geht oder ob es deswegen nicht (ganz) funktioniert bleibt sich doch gleich.
Du hast offensichtlich *immer* noch nicht begriffen, dass die Eigenschaften eines Netzes zu den Dingen gehoeren, auf die ein mit Netzwerken *arbeitendes* Programm ruecksicht nehmen muss. EIn langsames Internet mag aergerlich sein - aber es ist ein himmelweiter Unterschied zu einem nicht vorhandenen oder "kaputten" Internet.
Py-Prog
User
Beiträge: 673
Registriert: Dienstag 16. Februar 2010, 17:52
Wohnort: G:\ermany

deets hat geschrieben:Du hast offensichtlich *immer* noch nicht begriffen, dass die Eigenschaften eines Netzes zu den Dingen gehoeren, auf die ein mit Netzwerken *arbeitendes* Programm ruecksicht nehmen muss. EIn langsames Internet mag aergerlich sein - aber es ist ein himmelweiter Unterschied zu einem nicht vorhandenen oder "kaputten" Internet.
Was ist besser ein "schönes" nichtfunktionierendes Programm oder ein unschönes meist funktionierende Programm? Ich weiß das es eigentlich immer funktionieren müsste und das es mit allem was passieren kann zurecht kommen muss, aber das geht nicht weil es dann zu langsam ist. Ich könnte nur noch ein unschönes Programm schreiben das funktioniert und schnell genug ist, aber das ist ja dann unschön und sowas darf man ja nicht hernemen auch wenn es schön aus irgendeinen Grund nicht geht. :roll:
Technik ist: wenn alles funktioniert und keiner weiß warum.
Wer Rechtschreibfehler findet darf sie behalten.
BlackJack

@Py-Prog: Wie kommt denn jetzt die Geschwindigkeit des Programms ins Spiel? Wo hast Du denn etwas unschön und kaputt implementiert, weil es ordentlich zu langsam wäre?

Es ist nicht *irgendein* Grund warum es „schön” nicht geht. Das geht nämlich. Nur kannst oder willst Du das nicht machen. Wobei es hier auch nicht wirklich um ein subjektives „schön”, sondern schlicht um das objektive „robust” geht. Letztendlich ob das Programm kaputt ist oder nicht.
Antworten