Esta página aún no está traducida: se muestra la versión en inglés.
PlayServ SDK
El backend de juego que se entrega junto con el gameplay. PlayServ es un backend como servicio para juegos en vivo: un estudio opera el backend de su juego (datos, jugadores, salas, emparejamiento, comercio) sin tener que alojarlo. Con el SDK, tu código, en el servidor y en el motor, trabaja con esa plataforma.
La simulación no es tu código
Las salas, las colisiones, el desplazamiento, la predicción y la sincronización se ejecutan dentro de la plataforma. Tu juego se compone de declaraciones (entidades, mapas, habilidades, tablas de botín, política de sincronización), hooks (tus reglas, que se llaman en pasos con nombre), eventos (te suscribes en lugar de consultar) y operaciones (lo que pides u ordenas). Cada página de módulo se organiza exactamente alrededor de esas cuatro formas.
Lo que te ahorras es concreto: un bucle de juego, el armado de snapshots, un codificador de deltas, la resolución de colisiones, la integración del movimiento, la gestión de reconexiones, la validación de impactos con compensación de lag. Un juego como Tanks cabe en unas ~300 líneas, y en Primeros pasos lo construimos.
Modificar el estado declarado ya es la llamada de red
No existe una llamada de envío. Declaras cómo se sincroniza un campo con un atributo junto a él, y cambiar el campo ya es la operación de red. Los deltas respecto del último estado confirmado, la política por aspecto, la prioridad y la frecuencia de envío, la ventana de retención y los hooks de cambio se derivan de esa declaración; no escribes nada de eso.
Lo mismo vale para todo lo demás que se declara: un evento, un RPC, un grupo, un eje de una tabla de clasificación. Una declaración se convierte en la API tipada de cada binding, en una pantalla del panel de administración, en pasos con nombre que tus hooks pueden reemplazar, en reglas de acceso por actor y en código generado para cada binding. Por eso versionas la declaración y no el código generado. Consulta Data & Subscriptions y Esquema como código.
Las interfaces dependen del actor, no del lado
No hay un SDK de cliente y otro de servidor. Se distribuye un solo SDK, y lo que puede hacer una llamada lo decide el actor que está detrás: un jugador, un servicio, el cerebro de un bot, un operador.
Está pensado para el caso de la máquina de un jugador que crea una sala (room) y después la ejecuta: un cliente maestro, con las interfaces de dueño de la sala y nada más. Un build room-visitor no puede expulsar ni cerrar: esas interfaces no existen, no es que estén deshabilitadas.
Los derechos se componen de permisos atómicos, así que no hay niveles de rol predefinidos, y un rol restringe el acceso a los datos hasta la fila y la columna. Consulta Quién toma las decisiones, y Access & Roles para ver cómo se escriben los permisos otorgados (grant).
Un diseño, un stack
Cómo se escribe
El SDK es un solo diseño con dos maneras de acotarlo, y nada baja de nivel mientras el nivel de arriba pueda expresarlo. Los principios comunes son idénticos en todos los bindings. La forma de un lenguaje toma solo lo que su paradigma no puede expresar de la manera común. La forma de un motor toma solo lo que el motor cambia por encima de su lenguaje: en Unreal, una declaración viaja dentro de la propia macro de reflexión del motor.
Cómo se ejecuta
Tu código de juego se dirige a módulos y a nada más. Los módulos se arman con cuatro primitivas: eventos, RPC, datos y suscripciones, grupos. Debajo de ellas está el hub, al que nunca llamas: inyección de dependencias, montaje de módulos, la sesión, la recuperación de estado, la calidad de servicio de los mensajes. Más abajo están los adaptadores de transporte (transport adapter), uno por protocolo; cuál de ellos lleva una llamada no es algo que tu código decida ni note.
Las dos mitades completas: Cómo está construido el SDK, que termina en Bajo el capó.
Los módulos se componen; nada se hereda
No hay un módulo base del cual derivar ni una jerarquía que extender: los módulos forman un grafo, porque las funcionalidades reales cruzan ramas. El emparejamiento (matchmaking) reserva lugares en las salas, el botín coloca artículos a través del mapa, un chat vive dentro de una sala.
Aquí “herencia” abarca cuatro mecanismos distintos, y conviene distinguirlos: los RPC de una entidad son parte de la entidad; un preset es un paquete de aspectos con nombre, no una clase base; para sobrescribir un paso de la plataforma, le pones un atributo a tu versión; un módulo toma prestado otro mediante un decorador que acota la interfaz prestada. Consulta Herencia y composición.
Quién ve qué se declara, no se filtra en el cliente
Con cuarenta jugadores, un snapshot de toda la sala funciona bien; con doscientos, no. Las reglas de área de interés (interest rules) deciden quién recibe qué porción. Los paquetes por actor y la difusión (broadcast) son dos modos de entrega de un mismo modelo declarado: pasar de uno a otro es configuración, no reescritura. Lo que está lejos pierde detalle por niveles de detalle (detail tier) declarados antes de desaparecer.
Además, es una propiedad de seguridad: el estado que no debe filtrarse nunca se envía. La niebla de guerra y los campos exclusivos del dueño no están en el paquete; no es que se oculten en el cliente. Los espectadores, los administradores y las repeticiones obtienen una vista más amplia porque tienen permisos otorgados (grant) más amplios. Consulta Visibility.
Qué sobrevive a la pérdida de un host
Que un host se caiga no termina la partida. El estado de la sala no se copia entre hosts durante el juego: con un único dueño, el orden se mantiene sin necesidad de consenso. La partida sobrevive porque su estado está declarado, y el estado declarado se guarda fuera del host, con un snapshot en cada intervalo declarado. Un host de reemplazo continúa desde el último snapshot.
El reemplazo tiene todo el estado tal como estaba en ese snapshot, así que se pierde lo que se jugó desde entonces. Un deploy usa el mismo mecanismo sin esa pérdida: retirar un host de servicio (drain) es recorrer a propósito el camino de la conmutación por error.
El estado que solo vive en el motor no se recupera
El estado que guardas solo en actores del motor no está declarado, así que un host de reemplazo no lo tiene. ::: Consulta Qué sobrevive a la pérdida de un host, y Rooms para conocer el período de gracia (grace window) durante el cual un jugador puede volver a entrar.
Cualquier paso de la plataforma puede ser tuyo
Cada escenario de la plataforma es una cadena de funciones registradas, y puedes reemplazar un eslabón o envolverlo. El inicio de sesión, la validación de entrada, la compra, el envío, la carga de archivos: cada uno es un paso con nombre. Tu reemplazo se declara con un atributo, las versiones se eligen por condición y el paso propio de la plataforma queda como respaldo.
En lugar de hacer un fork del código fuente de la plataforma, reemplazas sus pasos. Consulta Extensibility.
Una interfaz, seis lenguajes
Un contrato, seis proyecciones: C#, TypeScript, Python y Go en el servidor; Unreal C++ y Unity C# generados en el motor. Cada ejemplo de código de este sitio muestra los seis, y cuando un binding no tiene interfaz para un paso, la pestaña explica por qué: el paso se ejecuta fuera del motor, o el derecho a hacer esa llamada lo tiene otro actor.
Dos consecuencias que conviene conocer antes de elegir un lenguaje. Los RPC reciben objetos del SDK por referencia en lugar de DTO aplanados. Y las primitivas asíncronas vienen incluidas: canales, flujos (stream) y direccionamiento a grupos, así que puedes hablarle a todo un grupo y recoger las respuestas, o consumir un archivo por fragmentos mientras todavía se está subiendo.
También hay un host determinista en memoria que ejecuta tu código de juego sin ningún backend detrás y con el tiempo bajo tu control, así que las pruebas son deterministas. Consulta Hilos, ciclo de vida y pruebas.
Lo que deliberadamente no está en el SDK (deploys, facturación, administración de organizaciones y usuarios) vive en el plano de operador. La barra lateral es el mapa de todo lo demás; Primeros pasos es el camino más corto para empezar.