From 1ad9ac98db0c5fd62ad6bf5e28b3bf4b87a4a489 Mon Sep 17 00:00:00 2001 From: marcos Date: Thu, 19 Feb 2026 20:15:50 +0100 Subject: [PATCH] docs: Add file and line references to all code blocks in README - Add specific line references to servidor.py code blocks - Add specific line references to app.py code blocks - Improve code traceability for email client documentation - Improve code traceability for game server documentation - Make documentation more navigable with precise locations --- README.md | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 028becb..91cd4a6 100644 --- a/README.md +++ b/README.md @@ -378,7 +378,7 @@ El servidor **identifica a cada jugador mediante su dirección de socket**, que #### ¿Cómo funciona en el mismo ordenador? ```python -# servidor.py - Al aceptar conexión +# servidor.py:62-65 - Al aceptar conexión conn, addr = s.accept() # addr = ('127.0.0.1', 49956) # El sistema operativo asigna un puerto efímero ÚNICO @@ -430,9 +430,10 @@ self.client_list = [] # Lista ordenada de direcciones └─────────────────────────────────────────────────────────────────┘ ``` -#### Código de Verificación (servidor.py) +#### Código de Verificación ```python +# servidor.py:222-232 - Verificación de turno al colocar bomba elif msg_type == 'PLACE_BOMB': if self.state == STATE_PLACING: # Obtener quién tiene el turno actual @@ -452,7 +453,8 @@ elif msg_type == 'PLACE_BOMB': El cliente guarda su propia dirección al conectarse y la compara con los mensajes del servidor: ```python -# app.py - Al conectarse +# app.py:1221-1240 - Identificación del cliente y manejo de turno +# Al conectarse self.my_address = str(sock.getsockname()) # Ej: "('127.0.0.1', 49956)" # Al recibir TURN_NOTIFY del servidor @@ -499,7 +501,7 @@ El servidor usa un **Set de Python** para almacenar las coordenadas de las bomba #### **Estructura de Datos: `self.bombs = set()`** ```python -# servidor.py - Línea 32 +# servidor.py:32 - Inicialización del set de bombas class GameServer: def __init__(self): self.bombs = set() # Set de tuplas (x, y) @@ -507,9 +509,10 @@ class GameServer: Un `set()` en Python **no permite elementos duplicados**. Si intentas agregar la misma tupla `(x, y)` dos veces, solo se guarda una vez. -#### **Validación al Colocar Bomba (servidor.py líneas 222-246)** +#### **Validación al Colocar Bomba** ```python +# servidor.py:222-246 - Validación completa de colocación de bomba elif msg_type == 'PLACE_BOMB': if self.state == STATE_PLACING: # 1. Verificar que es el turno del jugador @@ -691,9 +694,10 @@ Grid final: Total: 6 bombas únicas, sin duplicados ``` -#### **Código del Cliente (app.py líneas 1310-1330)** +#### **Código del Cliente** ```python +# app.py:1310-1330 - Manejo de clic en casilla durante fase de colocación def on_button_click(x, y): """Cuando el jugador hace clic en una casilla""" if self.game_phase == 'PLACING': @@ -717,6 +721,7 @@ def on_button_click(x, y): El servidor usa un `threading.Lock` para evitar condiciones de carrera cuando múltiples clientes envían mensajes simultáneamente: ```python +# servidor.py:28-47 - Sincronización con Lock class GameServer: def __init__(self): self.lock = threading.Lock() # Mutex para sincronización @@ -919,7 +924,7 @@ class GameServer: Protocolo para **leer correos** del servidor. Puerto: **143** (sin TLS). ```python -# Conexión IMAP (app.py líneas 1578-1582) +# app.py:1578-1582 - Conexión IMAP import imaplib self.imap_connection = imaplib.IMAP4(host, port_num) # Puerto 143 self.imap_connection.login(username, password) @@ -937,7 +942,7 @@ self.imap_connection.login(username, password) Protocolo para **enviar correos**. Puerto: **25** (sin TLS). ```python -# Conexión SMTP (app.py líneas 2949-2955) +# app.py:2949-2955 - Conexión y envío SMTP import smtplib with smtplib.SMTP(smtp_host, smtp_port_num, timeout=15) as server: server.send_message(msg, to_addrs=recipients) @@ -950,7 +955,7 @@ with smtplib.SMTP(smtp_host, smtp_port_num, timeout=15) as server: #### **Guardado Automático con Base64** ```python -# GUARDAR (app.py líneas 1540-1557) +# app.py:1540-1557 - Función _save_mail_credentials() import base64 config = { 'imap_host': '10.10.0.101', @@ -964,7 +969,7 @@ json.dump(config, open('.mail_config.json', 'w'), indent=2) ``` ```python -# CARGAR (app.py líneas 1512-1520) +# app.py:1512-1520 - Función _load_mail_credentials() config = json.load(open('.mail_config.json', 'r')) password = base64.b64decode(config['password']).decode() # Decodificar ``` @@ -1138,6 +1143,7 @@ fetch(mail_id, '(BODY.PEEK[] FLAGS)') Después de enviar un correo por SMTP, se guarda una copia en la carpeta "Sent" del servidor IMAP para que aparezca en Webmin y otros clientes. ```python +# app.py:2990-3015 - Guardar correo en carpeta Sent del servidor # Intentar múltiples nombres de carpeta sent_folders = ['Sent', 'INBOX.Sent', 'Enviados', 'INBOX.Enviados', 'Sent Items'] @@ -1199,9 +1205,10 @@ if not folder_found: ### 👥 Envío a Múltiples Destinatarios -#### **Validación y Parsing (líneas 2703-2740)** +#### **Validación y Parsing** ```python +# app.py:2703-2740 - Validación de múltiples destinatarios # Entrada del usuario to_addr_raw = "marcos@psp.es, user2@example.com; user3@test.org" @@ -1282,6 +1289,7 @@ self._send_mail_with_attachments(recipients, subject, body, attachments) #### **Adjuntar Archivos** ```python +# app.py:2568-2590 - Adjuntar archivos con diálogo # Usuario hace clic en "📎 Adjuntar archivo" file_paths = filedialog.askopenfilenames( title='Seleccionar archivos', @@ -1296,7 +1304,7 @@ file_paths = filedialog.askopenfilenames( # Se guardan en lista attachments.append(file_path) -# Al enviar, se procesan: +# Al enviar, se procesan (app.py:2895-2920): for file_path in attachments: file_name = os.path.basename(file_path) # "documento.pdf" file_ext = os.path.splitext(file_path)[1] # ".pdf" @@ -1313,6 +1321,7 @@ for file_path in attachments: #### **Imágenes Inline con Ctrl+V** ```python +# app.py:2609-2645 - Pegar imagen desde portapapeles # Usuario copia una imagen y presiona Ctrl+V def on_paste(event): try: @@ -1394,7 +1403,8 @@ def on_paste(event): ### 🔵 Sistema de Indicadores Visuales ```python -# Al cargar correos (líneas 1775-1790) +# app.py:1775-1790 - Indicadores visuales de correos leídos/no leídos +# Al cargar correos for mail in mail_list: if is_seen: # Correo leído @@ -1451,7 +1461,7 @@ self.unread_label.config(text=f'Correos sin leer: {self.unread_count}') ### 🛡️ Manejo de Errores y Log ```python -# Función de log (líneas 4304-4315) +# app.py:4304-4315 - Función _log() con verificación de widget def _log(self, text: str) -> None: # Verificar si estamos en hilo principal if threading.current_thread() is not threading.main_thread():