PHP: parse_str_packet (Spanish)

May 27 2004

https://github.com/soywiz/phprobot

parse_str_packet es una función que he hecho específicamente para el bot del ragnarok que estoy haciendo. Depende de otras funciones de stream de cadenas sencillitas que creé para desempaquetar números, cadenas y tipos sencillos.

parse_str_packet recibe dos parámetros: &$d y $fmt. El primer parámetro $d al igual que en el resto de funciones para desempaquetar es una referencia a una variable de tipo cadena que contiene el paquete. En php una referencia es equivalente a un alias y se necesita para acceder a variables cuyo nombre desconoces en las funciones. Y es muy útil (y mas rápido) para acceder por ejemplo a variables que se encuentran en… $ejemplo['valor1'][0]['valor2'], requiere menos proceso haces una referencia y ya está.

El otro parámetro es otra cadena que contiene el formato de los paquetes y aquí está lo interesante.

Tengo un fichero que contiene “la definición” de los paquetes que ya estoy parseando con funciones. Y haré que en vez de llamar a las funciones se utilice ese formato que llamando a la función genera un array con valores asociativos y ya parseados.

Por ejemplo, el primer paquete de todos que se recibe es el 0069

Yo en el fichero tengo una línea así.

0069 a[login_id;account_id;login_id2;last_login;account_sex;servers]llll-z[24]w-bx[rest][a[host;port;name;users;main;newn]rlnf[ip]wz[20]www]

Es bastante dificil de comprender, pero ésto permite que el código quede mucho mas claro, sin necesidad de 20 líneas para parsear el código ni nada por el estilo.

El funcionamiento es el siguiente:

se va recorriendo la cadena en busca de caracteres que “hacen cosas” por ejemplo: * a[lista separada por ;]a sirve para definir el nombre de las claves de los parámetros que se van a definir (por orden). * l extrae un paquete DWORD (32 bytes) de la cadena en el orden local (al menos que se defina lo contrario) * w es como l pero un WORD (16 bytes) * b es como w y l pero un BYTE (8 bits) * - elimina el parámetro parseado anteriormente. Por ejemplo un parámetro que no se necesita se puede extraer. O simplemente que se mantiene por compatibilidad pero no se requiere. * z[X] extrae un stringZ en los prox X caracteres. Se puede utilizar el parámetro “rest” para que extraiga el resto de la cadena. * s[X] lo mismo que z[X] pero se queda con toda la cadena en vez de solo con los caracteres antes de un NULL (0) * r invierte el orden de extracción de paquetes al de LOCAL, utiliza el de INTERNET. (utilizado para IPs) * n utiliza el orden de paquetes LOCAL * f[funcion] aplica un filtro al último parámetro indicado (llama a una función de php utilizando como parámetro el último paquete). Se define ‘ip’ como sinónimo de long2ip.

Ahora viene lo mas potente :)

  • x[repeat][expresion] extrae REPEAT veces una expresión “EXPRESION” y introduce el resultado en ese parámetro como un array interno. REPEAT permite utilizar un número, “rest” para hacerlo hasta que se acabe la cadena o utilizando un parámetro anteriormente definido. Por ejemplo si quieremos que se repita el número de veces que se haya definido 2 paquetes antes se utiliza como REPEAT: p:2

Bueeeeeno y esto es todo. Por ahora he podido definir todos los paquetes, si alguno no puedo supongo que retocaría un poco esta función (que la he incluido en el sistema PHPWiz).

El objetivo de esto es que las funciones de recepción simplemente hagan cosas y que no se dediquen a parsear. Es mas lento, pero tiene muchas ventajas (anteriormente citadas ;) ) y otras como poder, cambiando solamente un fichero, hacer cambios pequeños en el protocolo sin cambiar el código.

En cualquier caso tal y como está hecho ya mola muxo a diferencia de otros bots que tienen un gran SWITCH donde definen el tamaño de los paquetes y ahí extraen todo. Gracias a la potencia de php, simplemente defino una función, por ejemplo:

recv_serv_0073

y en runtime PHP me detecta la función y la asocia al paquete (sin mas que definirla [no hay que ponerla en ningún otro sitio]). Utilizo además un array con todos los tamaños de los paquetes y eso extrae el paquete utilizando una clase genérica. Obtiene el ID del paquete se va a la lista… obtiene el tamaño, si es un tamaño variable se sacan los 2 siguientes bytes, y se saca un string con el paquete y el ID. Con eso se llama a la función.

También he visto que esa forma de crear paquetes es un poco criptográfica y hace que si no se conoce exactamente esos tamaños se pierda información y no se pueda recuperar (a diferencia del protocolo del wow) que siempre dice el tamaño del paquete de forma que aunke lo desconozcas puedas pasar de el y seguir con otro. ¿Quejas? NO. Gravity así se asegura de que es un pokito mas dificil hacer un cliente ;)

Bueno eso es todo mas o menos. He visto que el mensaje de antes se ha puesto mal, inicialmente he pensado que era el encoding pero ya he visto ke no.

ACTUALIZACIÓN: Sí, era el encoding… pero del formulario donde he puesto el post :P luego he actualuzado el Encoding, he estado mirando cual era el que necesitaba:

Y he visto un sitio: http://www.intellidimension.com/default.rsp?topic=/pages/rdfgateway/reference/script/response_charset.rsp

He cambiado el encoding a iso-8859-1 he visto que también servía el windows-1252 pero la palabra windows… como ke… :P ISO suena mejor ;) ad+ ISO para algo están los estándares :D