Esta página aún no está traducida: se muestra la versión en inglés.
Bajo el capó: transporte y hub
Esta página es una referencia de arquitectura, no una interfaz a la que llamas. Nada de lo que describe aparece en la API con la que programas: no hay socket, canal, envoltorio ni reintento que tengas que gestionar. Conceptos básicos nombra las capas; el mecanismo solo se describe aquí, para que un arquitecto pueda comprobar qué hace el SDK con una conexión caída, un módulo deshabilitado o un mensaje que debe llegar exactamente una vez.
La pila de capas
Cinco capas, de arriba abajo: el espacio de usuario, los módulos, las primitivas, el hub y, debajo de él, los adaptadores de transporte. Las dos de arriba son espacio de usuario; todo lo de abajo es asunto del propio SDK.
- El espacio de usuario es tu código. Ve módulos, y el vocabulario termina ahí.
- Los módulos son la capa de aplicación: salas, emparejamiento, inventario, tablas de clasificación. Forman un grafo, no un árbol, y eso es lo que el hub tiene que resolver cuando se apaga uno de ellos (Herencia y composición describe esa forma).
- Las primitivas son la primera implementación, a la que todo hace referencia: datos, eventos, RPC, grupos. Un módulo es un conjunto de primitivas con nombre, más sus propias reglas.
- El hub es el controlador: inyección de dependencias, montaje de módulos, la sesión del usuario, la recuperación de estado, la calidad de servicio de los mensajes y el enrutamiento de cada mensaje entrante al módulo montado para recibirlo.
- Los transportes son adaptadores sobre un protocolo. Hay varios, y el hub los trata a todos por igual.
Los transportes son adaptadores
Habrá más de un transporte, y se diferencian en cosas que, de otro modo, se filtrarían a cada módulo:
| Eje | Opciones |
|---|---|
| Forma | orientado a mensajes u orientado a solicitudes |
| Canales | un solo canal o varios canales |
| Estado | con recuperación del estado de la conexión, o sin ella |
| Protocolo | TCP o UDP |
Hoy eso significa WebSocket, el transporte UDP de PlayServ y HTTP simple. Cada uno es un adaptador que oculta sus propios detalles de implementación, y todos exponen lo mismo hacia arriba: una sesión de transporte. El hub tiene una sesión, nunca un socket, así que nada por encima del adaptador depende de la interfaz de red.
Una sola sesión de transporte a la vez
Un canal de transporte y una sesión de transporte a la vez. Usar un transporte de backend para las tablas de clasificación mientras un transporte de cliente maestro lleva la sesión en vivo queda fuera del alcance, y ninguna firma de la API depende de que haya varios canales abiertos. Hasta que una versión posterior diga lo contrario, el contrato es una sola sesión.
El hub oculta el transporte por completo
Hacia abajo, el hub habla la interfaz del transporte. Hacia arriba, ofrece estado, eventos y la sesión del usuario. Ni el código de los módulos ni el del juego pueden saber qué transporte hay debajo, cómo agrupó el hub una llamada en un lote ni qué hizo para volver a un estado consistente después de una interrupción.
- La sesión del usuario pertenece al hub, no a un módulo. La reconexión, la reanudación y la recuperación de estado ocurren una sola vez, en el hub, para todo lo que está montado en él.
- La QoS de los mensajes pertenece al hub, no al módulo de datos. Los envoltorios, los reintentos y el empaquetado son mecánica del hub.
QoS de mensajes: tres niveles
Un módulo declara solo la garantía de entrega que necesita:
| Nivel | Significado |
|---|---|
at least once | se reenvía hasta que llega la confirmación; el receptor tolera duplicados |
at most once | se envía una vez y nunca se reintenta; la pérdida es aceptable |
exactly once | con deduplicación y confirmación; es el nivel más costoso y se usa donde hace falta |
Cómo se cumple la garantía es asunto del hub, no del módulo ni tuyo.
DI, montaje y módulos deshabilitados
El hub crea las instancias de los módulos y los monta (en la raíz o en un espacio de nombres), y resuelve las dependencias de cada módulo respecto de las primitivas y de otros módulos. Un build que no necesita un módulo no lo monta. El montaje usa espacios de nombres, y si un segundo módulo reclama un punto de montaje (mount point) ya ocupado, se rechaza al montar: la composición falla ahí, nunca en la primera llamada.
Como los módulos forman un grafo, apagar uno tiene consecuencias aguas abajo, y el hub toma exactamente uno de dos caminos:
- Deshabilitar la cadena dependiente. Cada módulo que necesita el que falta también se apaga, y sus interfaces no existen, en lugar de fallar.
- Declarar funcionalidad degradada. Los módulos dependientes siguen montados y anuncian lo que ya no pueden hacer.
No hay un tercer camino: un módulo montado nunca descarta en silencio las operaciones que ya no puede hacer, así que una dependencia deshabilitada siempre es observable.
Lo que queda por encima de la línea
Cada promesa de las páginas de módulo se cumple por encima de ella: una mutación de entidad es la operación de red, un hook es una función tipada, entrar a una sala es una sola llamada. Puede que te encuentres con los nombres de las capas (Conceptos básicos remite aquí), pero nada por debajo de la línea es una llamada que haga tu código. Esas capas existen para que las promesas de arriba sobrevivan a un cambio de transporte.
Lo que sí vas a encontrar (el contexto de entrega en el que corren tus handlers, cuándo termina un handle y la implementación en memoria contra la que pruebas) está una página más arriba: Hilos, ciclo de vida y pruebas.