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
This commit is contained in:
marcos 2026-02-19 20:15:50 +01:00
parent 1006498c94
commit 1ad9ac98db
1 changed files with 24 additions and 14 deletions

View File

@ -378,7 +378,7 @@ El servidor **identifica a cada jugador mediante su dirección de socket**, que
#### ¿Cómo funciona en el mismo ordenador? #### ¿Cómo funciona en el mismo ordenador?
```python ```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) conn, addr = s.accept() # addr = ('127.0.0.1', 49956)
# El sistema operativo asigna un puerto efímero ÚNICO # 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 ```python
# servidor.py:222-232 - Verificación de turno al colocar bomba
elif msg_type == 'PLACE_BOMB': elif msg_type == 'PLACE_BOMB':
if self.state == STATE_PLACING: if self.state == STATE_PLACING:
# Obtener quién tiene el turno actual # 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: El cliente guarda su propia dirección al conectarse y la compara con los mensajes del servidor:
```python ```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)" self.my_address = str(sock.getsockname()) # Ej: "('127.0.0.1', 49956)"
# Al recibir TURN_NOTIFY del servidor # 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()`** #### **Estructura de Datos: `self.bombs = set()`**
```python ```python
# servidor.py - Línea 32 # servidor.py:32 - Inicialización del set de bombas
class GameServer: class GameServer:
def __init__(self): def __init__(self):
self.bombs = set() # Set de tuplas (x, y) 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. 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 ```python
# servidor.py:222-246 - Validación completa de colocación de bomba
elif msg_type == 'PLACE_BOMB': elif msg_type == 'PLACE_BOMB':
if self.state == STATE_PLACING: if self.state == STATE_PLACING:
# 1. Verificar que es el turno del jugador # 1. Verificar que es el turno del jugador
@ -691,9 +694,10 @@ Grid final:
Total: 6 bombas únicas, sin duplicados Total: 6 bombas únicas, sin duplicados
``` ```
#### **Código del Cliente (app.py líneas 1310-1330)** #### **Código del Cliente**
```python ```python
# app.py:1310-1330 - Manejo de clic en casilla durante fase de colocación
def on_button_click(x, y): def on_button_click(x, y):
"""Cuando el jugador hace clic en una casilla""" """Cuando el jugador hace clic en una casilla"""
if self.game_phase == 'PLACING': 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: El servidor usa un `threading.Lock` para evitar condiciones de carrera cuando múltiples clientes envían mensajes simultáneamente:
```python ```python
# servidor.py:28-47 - Sincronización con Lock
class GameServer: class GameServer:
def __init__(self): def __init__(self):
self.lock = threading.Lock() # Mutex para sincronización self.lock = threading.Lock() # Mutex para sincronización
@ -919,7 +924,7 @@ class GameServer:
Protocolo para **leer correos** del servidor. Puerto: **143** (sin TLS). Protocolo para **leer correos** del servidor. Puerto: **143** (sin TLS).
```python ```python
# Conexión IMAP (app.py líneas 1578-1582) # app.py:1578-1582 - Conexión IMAP
import imaplib import imaplib
self.imap_connection = imaplib.IMAP4(host, port_num) # Puerto 143 self.imap_connection = imaplib.IMAP4(host, port_num) # Puerto 143
self.imap_connection.login(username, password) self.imap_connection.login(username, password)
@ -937,7 +942,7 @@ self.imap_connection.login(username, password)
Protocolo para **enviar correos**. Puerto: **25** (sin TLS). Protocolo para **enviar correos**. Puerto: **25** (sin TLS).
```python ```python
# Conexión SMTP (app.py líneas 2949-2955) # app.py:2949-2955 - Conexión y envío SMTP
import smtplib import smtplib
with smtplib.SMTP(smtp_host, smtp_port_num, timeout=15) as server: with smtplib.SMTP(smtp_host, smtp_port_num, timeout=15) as server:
server.send_message(msg, to_addrs=recipients) 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** #### **Guardado Automático con Base64**
```python ```python
# GUARDAR (app.py líneas 1540-1557) # app.py:1540-1557 - Función _save_mail_credentials()
import base64 import base64
config = { config = {
'imap_host': '10.10.0.101', 'imap_host': '10.10.0.101',
@ -964,7 +969,7 @@ json.dump(config, open('.mail_config.json', 'w'), indent=2)
``` ```
```python ```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')) config = json.load(open('.mail_config.json', 'r'))
password = base64.b64decode(config['password']).decode() # Decodificar 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. 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 ```python
# app.py:2990-3015 - Guardar correo en carpeta Sent del servidor
# Intentar múltiples nombres de carpeta # Intentar múltiples nombres de carpeta
sent_folders = ['Sent', 'INBOX.Sent', 'Enviados', 'INBOX.Enviados', 'Sent Items'] sent_folders = ['Sent', 'INBOX.Sent', 'Enviados', 'INBOX.Enviados', 'Sent Items']
@ -1199,9 +1205,10 @@ if not folder_found:
### 👥 Envío a Múltiples Destinatarios ### 👥 Envío a Múltiples Destinatarios
#### **Validación y Parsing (líneas 2703-2740)** #### **Validación y Parsing**
```python ```python
# app.py:2703-2740 - Validación de múltiples destinatarios
# Entrada del usuario # Entrada del usuario
to_addr_raw = "marcos@psp.es, user2@example.com; user3@test.org" 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** #### **Adjuntar Archivos**
```python ```python
# app.py:2568-2590 - Adjuntar archivos con diálogo
# Usuario hace clic en "📎 Adjuntar archivo" # Usuario hace clic en "📎 Adjuntar archivo"
file_paths = filedialog.askopenfilenames( file_paths = filedialog.askopenfilenames(
title='Seleccionar archivos', title='Seleccionar archivos',
@ -1296,7 +1304,7 @@ file_paths = filedialog.askopenfilenames(
# Se guardan en lista # Se guardan en lista
attachments.append(file_path) attachments.append(file_path)
# Al enviar, se procesan: # Al enviar, se procesan (app.py:2895-2920):
for file_path in attachments: for file_path in attachments:
file_name = os.path.basename(file_path) # "documento.pdf" file_name = os.path.basename(file_path) # "documento.pdf"
file_ext = os.path.splitext(file_path)[1] # ".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** #### **Imágenes Inline con Ctrl+V**
```python ```python
# app.py:2609-2645 - Pegar imagen desde portapapeles
# Usuario copia una imagen y presiona Ctrl+V # Usuario copia una imagen y presiona Ctrl+V
def on_paste(event): def on_paste(event):
try: try:
@ -1394,7 +1403,8 @@ def on_paste(event):
### 🔵 Sistema de Indicadores Visuales ### 🔵 Sistema de Indicadores Visuales
```python ```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: for mail in mail_list:
if is_seen: if is_seen:
# Correo leído # Correo leído
@ -1451,7 +1461,7 @@ self.unread_label.config(text=f'Correos sin leer: {self.unread_count}')
### 🛡️ Manejo de Errores y Log ### 🛡️ Manejo de Errores y Log
```python ```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: def _log(self, text: str) -> None:
# Verificar si estamos en hilo principal # Verificar si estamos en hilo principal
if threading.current_thread() is not threading.main_thread(): if threading.current_thread() is not threading.main_thread():