protocol.sh (3984B)
1 #!/usr/bin/env bash 2 # ============================================================================ 3 # lib/protocol.sh -- El "idioma" que hablan dos computadoras y como leerlo. 4 # ============================================================================ 5 # 6 # Cuando dos personas juegan en red, sus dos programas se mandan MENSAJES de 7 # texto, uno por linea. Para entenderse, los dos tienen que usar el mismo 8 # vocabulario. Eso es un "protocolo": una lista acordada de mensajes posibles. 9 # 10 # Estos son todos los mensajes que existen (cada uno ocupa una linea): 11 # HELLO <id> saludo inicial / negociar quien juega cada color 12 # HELLO host <tc> del que hace de servidor al que se conecta: ademas lleva el 13 # control de tiempo ("off" o "BASE:INC" en segundos, ej "300:5") 14 # HELLO join del que se conecta al servidor, al conectarse 15 # HELLO rejoin de quien vuelve, al reconectarse a una partida cortada 16 # COLOR <w|b> el lider le dice al otro que color le toca jugar 17 # TIME <tc> modo relay: las blancas avisan el control de tiempo elegido 18 # STATE <st> el lider manda el estado COMPLETO de la partida (posicion, 19 # relojes, etc.) para que el otro arranque igual o se reconecte 20 # MOVE <uci> una jugada, ej "MOVE e2e4" o "MOVE e7e8q" (coronacion) 21 # RESIGN quien lo manda, se rinde 22 # DRAW50 quien lo manda, reclama tablas por la regla de 50 movimientos 23 # DRAWOFFER quien lo manda, ofrece tablas de comun acuerdo 24 # DRAWYES/DRAWNO el otro acepta / rechaza la oferta de tablas 25 # TIMEOUT a quien lo manda se le acabo el tiempo (pierde por tiempo) 26 # QUIT quien lo manda salio prolijamente (no es rendicion) 27 # 28 # COMO VIAJAN LOS MENSAJES (un poco de tecnica): 29 # Usamos un "coprocess" de bash: un segundo programa (ncat) que corre EN 30 # PARALELO al nuestro y mantiene la conexion de red abierta. Bash nos da dos 31 # "file descriptors" (numeritos que identifican canales de comunicacion) para 32 # hablarle: NET_R para LEER lo que llega, NET_W para ESCRIBIR lo que mandamos. 33 # Si NET_W esta vacio, quiere decir que no estamos jugando en red. 34 35 NET_R="" # canal de lectura (por aca nos llega lo que manda el otro) 36 NET_W="" # canal de escritura (por aca le mandamos cosas al otro) 37 38 # net_send: mandar un mensaje (una linea de texto) al otro jugador. 39 net_send() { 40 # Si no estamos en red (NET_W vacio), no hacemos nada y devolvemos exito. 41 # Asi el mismo codigo del juego sirve para una partida local sin tocar nada. 42 [[ -n "$NET_W" ]] || return 0 43 # "printf '%s\n' "$1"" imprime el mensaje y un salto de linea; ">&$NET_W" 44 # redirige esa salida hacia el canal de escritura (en vez de a la pantalla). 45 printf '%s\n' "$1" >&"$NET_W" 46 } 47 48 # net_recv: esperar y leer UNA linea del otro jugador. Se queda "bloqueado" 49 # (frenado) hasta que llegue algo. Devuelve 1 (error) si el otro corto (EOF). 50 net_recv() { 51 local line 52 # "read -r -u "$NET_R" line" lee una linea desde el canal NET_R y la guarda en 53 # "line". "IFS=" antes del read evita que se recorten espacios. Si el read 54 # falla (no hay mas datos, el otro se desconecto), devolvemos 1. 55 IFS= read -r -u "$NET_R" line || return 1 56 printf '%s' "$line" 57 } 58 59 # net_recv_timeout: igual que net_recv, pero con un LIMITE DE TIEMPO en segundos 60 # (1er argumento). Es util para no quedarnos esperando para siempre. 61 # Importante: conserva el codigo de error del read para que quien llama pueda 62 # distinguir DOS situaciones distintas: 63 # - se acabo el tiempo de espera -> read devuelve un numero MAYOR a 128 64 # - el otro se desconecto (EOF) -> read devuelve 1 65 # (Esa diferencia es clave para el reloj y para detectar caidas de conexion.) 66 net_recv_timeout() { 67 local line rc 68 IFS= read -r -t "$1" -u "$NET_R" line; rc=$? # "$?" es el codigo del ultimo comando 69 (( rc != 0 )) && return $rc # si fallo, devolvemos el MISMO codigo 70 printf '%s' "$line" 71 }