Video Summary and Transcription
La charla de hoy cubre la creación de juegos multijugador con Coliseus, Node.js y Typescript. Explora el estado de la red en la web, servidores alternativos y cómo funciona Coliseus. La charla también discute la predicción del lado del cliente, la compensación de la latencia, las limitaciones del servidor, la escalabilidad y muestra juegos geniales hechos con Coliseus. Además, menciona la integración de Nakama y Unity para la predicción del lado del cliente en juegos multijugador. Coliseus está disponible en móviles y se puede encontrar en colossus.io o en el repositorio de GitHub.
1. Introducción a Coliseus y Networking
Hoy, voy a hablar sobre cómo hacer juegos multijugador con Coliseus, Node.js y Typescript. Discutiremos el estado de la red en la web, servidores alternativos, cómo funciona Coliseus, técnicas del lado del cliente y juegos multijugador construidos con Coliseus. El estado de la red en la web actualmente depende de WebSockets y WebRTC, con la posible inclusión futura de Web Transport. Mientras que TCP es la única opción para juegos basados en la web, generalmente se prefiere UDP. Sin embargo, muchos juegos exitosos se han hecho usando TCP y WebSockets.
Hola, hola, a todos. Soy Enzo, el creador de Coliseus, y hoy voy a hablar sobre cómo hacer juegos multijugador con Coliseus, Node.js y Typescript. Estoy muy emocionado de estar aquí.
Entonces, los temas de esta charla son el estado de la red en la web, servidores alternativos, cómo funciona Coliseus y sus sistemas internos, algunas técnicas del lado del cliente que puedes aplicar incluso fuera del alcance de Coliseus, y algunos juegos multijugador construidos con Coliseus.
Entonces, el estado de la red en la web. Ahora mismo tenemos, para conexiones bidireccionales, tenemos principalmente WebSockets y WebRTC disponibles. Web Transport, con suerte, va a llegar en el futuro. WebSockets es solo TCP y está disponible desde 2011. WebRTC ha sido completamente estandarizado solo en el último año y es bastante complicado e involucra muchos otros protocolos y sí admite conexiones confiables y no confiables. WebTransport, con suerte, va a reemplazar a WebSocket en el futuro. Nadie sabe cuándo. Y es muy emocionante. Admite la entrega confiable y no confiable y tiene soporte experimental ya en Chrome desde enero de este año. El advice general para la red fuera de la web es que TCP no es ideal para juegos y UDP sí, es difícil estar en desacuerdo con esto, pero desafortunadamente la web solo tiene TCP por ahora. Con suerte, el transporte web cambiará eso en el futuro y muchos juegos exitosos se han hecho en el pasado usando TCP y WebSockets es lo que tenemos y hay muchos juegos exitosos hechos con WebSockets.
2. Servidores Alternativos y el Framework Coliseus
En un enfoque alternativo, el servidor valida y determina el estado del juego, en lugar de depender del cliente. Coliseus es un framework de Node.js que utiliza WebSockets para el transporte. Proporciona emparejamiento, sincronización de salas y mensajería. Las salas en Coliseus tienen métodos de ciclo de vida y pueden crearse cuando los clientes solicitan unirse o crearlas. El flujo de emparejamiento implica una solicitud HTTP para la reserva de asiento, la consulta de salas existentes, la creación de una sala si es necesario y el establecimiento de una conexión WebSocket. Una vez establecida la conexión, el cliente recibe el estado completo de la sala y puede comenzar a intercambiar mensajes.
Bien, sin más preámbulos hablemos de servidores alternativos. Así que en un enfoque alternativo no confiarías en el cliente, por lo que el cliente puede decir, por ejemplo, dónde está o el cliente nunca debería dictar información. El servidor siempre debería ser capaz de validar y decir la verdad sobre el estado del juego. Así que esto no es muy alternativo, podría estar bien si estás de acuerdo con esto, así que esto no sería factible en un juego competitivo multijugador.
Así que una alternativa a esto es dar más, dar menos información al servidor. Digamos que estoy apuntando a un cierto ángulo y avanzando, así que el servidor tiene la posición actual y va al servidor va a determinar cuál es la siguiente y no el cliente. Así que esto es alternativo, las responsabilidades del servidor son mantener la lógica del juego, el estado del juego, validar las entradas del cliente e intercambiar mensajes con los clientes y también el estado. Las responsabilidades del cliente es básicamente ser una representación visual de lo que está en el servidor y enviar entradas y acciones al servidor. Lo que Coliseus aporta a la mesa, es un Node.js framework. Está construido solo con WebSockets, así que solo hay WebSocket como capa de transporte hasta ahora en Coliseus. Empareja a los jugadores en salas y tiene una sincronización de salas incorporada y sistema de mensajes. Y tiene los bloques de construcción y la arquitectura para que puedas escalar esto a muchos servidores y tener muchos servidores que manejen múltiples salas. Y como puedes ver, las salas es un bloque muy básico de Coliseus, y así es como se ve una definición de sala, y tiene sus métodos de ciclo de vida, como onCreate para configurar una partida, onJoin cuando algún jugador se une a la sala, onLeave para eliminar a este jugador del estado de la sala y los otros clientes pueden reaccionar a este cambio, y onDispose cuando esta sala ha sido destruida en el servidor. Si tienes un estado global compartido o algo en la base de datos, este es un buen lugar para borrar cosas globales que esta sala posiblemente ha creado. Para que los clientes se unan a esta sala, necesitas exponer esto al emparejador. Verás que no se crea ninguna sala real en este punto. Las salas solo se crean cuando el cliente solicita unirse o crearlas. En este ejemplo, es el cliente el que solicita unirse a una sala de juego y proporciona información sobre sí mismo, como el nombre. Para la solicitud de emparejamiento, así es como se ve el flujo. El cliente hace una solicitud HTTP para pedir una reserva de asiento. El servidor va a consultar las posibles salas que ya existen. Si no existe, intenta crear una y devuelve el ID de la sala y el ID de la sesión, que es la reserva de la sesión. Después de obtener la reserva de la sesión, intenta conectarse realmente a través de web sockets. Así es como se ve el flujo desde la perspectiva del servidor. Primero intenta validar al usuario durante el inicio de sesión. Esto es totalmente personalizado y basado en tus propios requisitos. Si eso tiene éxito, intenta llamar a on join. En cualquier momento, podrías lanzar un error aquí y el cliente llegaría al bloque catch aquí. Sí, podrías lanzar un error desde el servidor y aquí iría en el cliente. No ha ocurrido ningún error durante este proceso, la conexión se establece, y después de que se establece la conexión, lo primero es que el cliente recibe el estado completo de la sala, por lo que el cliente ya puede construir la representación visual que esa sala tiene en el servidor y empezar a intercambiar mensajes y más parches de estado y más mensajes, y luego es regular web socket y cosas bidireccionales.
3. Escucha de Mensajes y Mutación de Estado
La API onMessage es bastante simple, permitiendo escuchar y enviar mensajes entre el cliente y el servidor. El servidor puede enviar mensajes a clientes específicos o transmitir mensajes a todos los clientes en una sala. El estado de la sala se basa en estructuras mutables, sincronizadas cada 15 milisegundos, y las mutaciones solo deben realizarse en el servidor. Las estructuras utilizadas para el estado se llaman esquema de Coliseus, que proporcionan un tipado fuerte, serialización incremental, baja salida y una API de callback en el lado del cliente. En este ejemplo, se utiliza un juego de mesa para ilustrar la creación de un estado y la asignación de jugadores basada en el ID de sesión.
La API onMessage es bastante simple, así es como escuchas y envías mensajes entre cliente y servidor. El servidor puede enviar un mensaje a un cliente en particular basado en la referencia del cliente, y el servidor también puede transmitir mensajes a todos los clientes dentro de esta sala, por lo que no hay una forma incorporada de que el servidor envíe la transmisión a todos los clientes en todas las salas. Si necesitas hacer eso, debes implementarlo por ti mismo.
El estado de la sala se basa en estructuras mutables, por lo que realmente no puedes usar ninguna estructura inmutable como lo harías en los frameworks de front-end, y las mutaciones que realizas en esas estructuras se sincronizan automáticamente, cada 15 milisegundos por defecto. Y esas mutaciones son unidireccionales, por lo que solo debes mutar los data del estado en el servidor, para que se transmitan de vuelta a los clientes. Nunca debes mutar el estado en el cliente mismo.
Bien, las estructuras que usamos para el estado se llaman esquema de Coliseus. Es de tipado fuerte, admite serialización incremental, tiene la salida más baja que podríamos implementar posiblemente, y proporciona una API de callback en el lado del cliente. Explicaré un poco sobre esto en un momento. Está muy inspirado en otras bibliotecas de serialización como protocol buffers, flat buffers, y otros. En este ejemplo, imagina un juego de mesa. Tienes un mapa de jugadores aquí, y el jugador tiene una posición. Una posición en el mapa, o en el tablero. Y en el code de la sala, inicialmente crearías un estado, por lo que estableces el estado durante la creación, y cada vez que un jugador se une a esta sala, asignamos al mapa de jugadores un nuevo jugador basado en el ID de sesión de ese cliente.
4. Predicción del lado del cliente y compensación de la latencia
El ID de sesión es el identificador único para este cliente en esta sala. En el lado del cliente, cada cliente debe escuchar las adiciones y eliminaciones de esta estructura de jugador. ¿Cuál es la diferencia entre los mensajes y el estado de la sala? Los mensajes son efímeros, mientras que el estado es persistente. En los juegos multijugador, la latencia es inevitable, y los desarrolladores deben intentar aliviar la latencia percibida por el jugador actual. La predicción del lado del cliente se puede utilizar para proporcionar una respuesta inmediata a las acciones del jugador. La interpolación lineal es una técnica simple que puede tener buenos resultados.
El ID de sesión es el identificador único para este cliente en esta sala. Y cada vez que el cliente envía un mensaje de movimiento, obtiene la referencia del jugador e incrementa su posición. Como puedes ver, esto no es muy alternativo. Cualquier jugador podría enviar esto en cualquier momento y se incrementaría su posición. Así que idealmente, debe haber algún tipo de validation para, como, ¿está este jugador en su turno actual? ¿Tiene algún movimiento válido pendiente? Algo así como algunas verificaciones.
Sí, entonces en el lado del cliente, cada cliente debería escuchar, como, adiciones y eliminaciones de esta estructura de jugador. Así que estos son los callbacks que registrarías en el cliente para preparar la representación visual. Esto está escuchando el cambio del atributo de posición. Una cosa genial de usar TypeScript es que si proporcionas, esto es opcional por cierto, si proporcionas la referencia de tipo para el estado de la sala aquí, tendrías autocompletado para todas las propiedades y todo dentro del estado durante el desarrollo.
Sí, entonces, ¿cuál es la diferencia entre los mensajes y el estado de la sala? Bueno, los mensajes son efímeros y no son persistentes en ningún lugar. Así que cada vez que envías un mensaje, solo los clientes conectados actualmente lo recibirán. El estado, por otro lado, es persistente. Si estableces algo en el estado y un nuevo cliente se une, ese nuevo cliente recibirá esos data. Correcto, entonces en los juegos multijugador la latencia es inevitable, y nosotros como desarrolladores debemos aceptarlo e intentar aliviar la latencia percibida por el jugador actual. Sí, entonces toma esto como que tienes al cliente en la posición 10-10, luego envía la acción para avanzar y el servidor aquí, recibe que está en 11-10, pero el cliente podría haberse movido inmediatamente aquí, y va a recibir de vuelta estos data del servidor solo un par de milisegundos después. Esto se llama tiempo de ida y vuelta, y sí, esto es medio tiempo de ida y vuelta. Y podrías usar este valor para realizar algunas técnicas en el lado del cliente, para intentar al menos dar al jugador una respuesta inmediata. Así que este ejemplo de Half the Opposite implementa la predicción del lado del cliente, que básicamente procesa las entradas de los jugadores inmediatamente en el lado del cliente. Así que cada vez que presionas las teclas para moverte, tu jugador se mueve inmediatamente. En el servidor, pone en cola las acciones del jugador y procesa la cola en cada tick del servidor. Sí, puedes echar un vistazo al code fuente allí. Es aplicable si quieres usar este enfoque fuera de Colesios también. Hay una presentación muy buena de GDC explicando el juego y el netcode de Overwatch. Así que te recomiendo mucho que lo veas. Explican toda la predicción del lado del cliente que hicieron allí. Y sí, también mencionan algunas técnicas para paquetes perdidos. Y realmente no podemos aplicar ninguna de estas en WebSockets porque no hay paquetes perdidos en una conexión confiable. Cuando tengamos en web transport, podríamos aplicar tales técnicas. Así que la interpolación lineal es muy simple y puede tener buenos resultados. En Mesmora, este juego que hice, utilicé la interpolación lineal en todas partes.
5. Limitaciones del servidor, escalabilidad y juegos geniales
Ves que este es un juego basado en mosaicos y el jugador se mueve a través de los mosaicos con interpolación lineal. Tener una física determinista es importante. Limitaciones del servidor, ¿cuántos CCU puede tener un servidor? Pude lograr eso en un servidor barato, un servidor WebSocket que ejecuta un juego de cartas/tablero que es muy lento en el ritmo de mensajería, el servidor podría manejar 3K conexiones concurrentes. Las salas en Coliseus son con estado y los juegos generalmente son con estado. Hay una capa de persistencia que permite a cualquier nodo hacer emparejamientos. Entonces, ¿cuántos CCUs puede manejar Koliasis? Depende. Recomendaría evitar tener un estado de sala muy grande y optimizar tus bucles de juego para usar la menor cantidad de ciclos de CPU posible. Algunos juegos geniales hechos con Coliseus: el juego de Tiny Dolby, el Shooter IO de código abierto, Raft Force, School Break y Kurka.io. Night's Edge de Lightfox Games.
Ves que este es un juego basado en mosaicos y el jugador se mueve a través de los mosaicos con interpolación lineal. El paso de tiempo bloqueado es muy importante para un juego basado en física. También se le llama tasa de ticker fija. Tener una física determinista es importante porque entonces podrías tener muchos clientes y el servidor si necesitas, simulando la física con diferentes FPS y obteniendo el mismo resultado.
Entonces, limitaciones del servidor, como cuántos... odio esta pregunta, ¿cuántos CCU puede tener un servidor? Quiero decir, puedes encontrar algún material en internet que la gente logró tener 1 millón de conexiones WebSocket en un solo servidor, pero eso no es muy realista. Podrías lograr eso principalmente con conexiones inactivas, sin intercambiar ningún mensaje, y eso no es realista en absoluto. Lo que pude lograr es que en un servidor barato, un servidor WebSocket que ejecuta un juego de cartas/tablero que es muy lento en el ritmo de mensajería, el servidor podría manejar 3K concurrent conexiones. Pero no recomendaría tener una sola sala con tantas conexiones. De 50 a 100 sería lo ideal, y si necesitas más conexiones que eso, puedes tener más salas en otros servidores para manejar esta carga. Entonces, sí, entonces entra en juego la escalabilidad. Y escalabilidad, las salas viven en memoria en Coliseus, por lo que las salas son con estado y los juegos son generalmente con estado, por lo que realmente necesitas, no puedes usar enfoques sin estado para, para juegos no entiendo cómo la gente siempre recomienda ser sin estado cuando se escalan cosas. Y hay una capa de persistencia que permite a cualquier nodo hacer emparejamientos. Entonces, para resumir, así es como funciona la reserva de semillas en un solo servidor y cuando tienes varios servidores, la solicitud de reserva de semillas llegaría al balanceador de carga y sería reenviada a cualquiera de los servidores activos. Y luego cualquiera de los servidores activos devolvería la reserva. Y entonces, teniendo esta información, podemos hacer la solicitud de WebSocket directamente al servidor en el que vive esa sala. Entonces, ¿cuántos CCUs puede manejar Koliasis? Depende. Sí, no lo sé. Depende de tu juego. Tienes límites de CPU y memoria, y todos los tienen, no importa si estás usando Koliasis o no. Entonces, para Koliasis en sí, recomendaría evitar tener un estado de sala muy grande, lo cual aumentaría el rendimiento de tus salas, y optimizar tus bucles de juego para usar la menor cantidad de ciclos de CPU posible. Entonces sí, ya me estoy quedando sin tiempo aquí. Las herramientas adicionales que tenemos son este monitor para desarrollo, puede ser realmente útil. Una prueba de carga para crear pequeños bots y pruebas, hasta dónde pueden llegar tus servidores con pruebas de carga automatizadas.
Algunos juegos geniales hechos con Coliseus. Este fue el primer juego bien hecho con Coliseus por Tiny Dolby, todavía está disponible aquí, creo que esto se lanzó en 2015. Esto es hecho por la comunidad, se llama el Shooter IO de código abierto, se puede encontrar aquí en este enlace. Raft Force, yo y Tiny Dobbins hicimos con Default Engine, también es un juego web disponible aquí. School Break hecho por Tobias, también está disponible para jugar aquí. Y Kurka.io es el primer shooter de jugadores que he visto que está usando Coliseus y es muy divertido. Y finalmente, Night's Edge de Lightfox Games.
6. Juego de Unity y Resultados de la Encuesta
Este es un juego de Unity, también disponible para descargar. Espero que hayas aprendido algo. Echemos un vistazo a los resultados de la encuesta. Parece que WebSockets es la opción principal para juegos multijugador. Socket.io debería haber estado en la lista. Muchas personas están usando Coliseus. ¿Qué es Nakama?
Este es un juego de Unity, también disponible para descargar. Y eso es todo. Espero que hayas aprendido algo. Estoy aquí, esperando tus preguntas. Muchas gracias.
Entonces, primero echemos un vistazo a los resultados de la encuesta para la pregunta que hicimos al principio. ¡Oh! Ninguno de ellos. Tenemos. Realmente me interesaba eso. Si eso significa que las personas no han hecho juegos multijugador, o si lo han hecho y simplemente no usaron una de estas opciones. Sí. Porque no sé qué más podrías estar usando. Sí. Esperaba que WebSockets fuera el líder. Lo siento, adelante. Bueno, así es como interpreto esto. Como para las personas que hicieron, creo, juegos multijugador, parece que WebSockets son la opción principal. Lo cual tiene sentido porque, al menos para mí, solo he hecho cosas en Socket.io, que yo interpreto como, básicamente es solo una capa de framework alrededor de WebSockets. Sí. Oh, sí. Socket.io debería haber estado en esta lista también. Sí, probablemente. Por eso me preguntaba si no estaba. No, está bien. Y luego hay un montón de gente usando, lo siento, ¿puedes pronunciarlo, Kolesius? ¿Kolesius? ¿Así es como se pronuncia? Sí, Kolesius, sí, correcto. Kolesius, sí. Gracias. Genial. Hay un montón de gente usando eso también.
Nakama y Desarrollo de Juegos Web
¿Y qué es Nakama? Ya están en Go, así que no mucha gente ha oído hablar de ello. El proceso de hacer juegos de navegador HTML5 puede diferir de colocar elementos en una pantalla a través de HTML, CSS, etc. Puedes tener un juego regular sin usar Canvas, pero es más común usar Canvas2D o WebGL. Hay muchas opciones para herramientas y marcos, incluyendo little.js, Pixie.js, Play Canvas y Babylon.
¿Y qué es Nakama? No he oído hablar de eso. ¿Cuál? ¿Perdón? Nakama. Nakama, sí, ya están en Go, así que creo que por eso no mucha gente ha oído hablar de ello. Son algo similares, pero ya están en Go. Genial, bueno saberlo.
Genial, creo que podemos pasar a algunas de las preguntas del público ahora. Así que creo que la primera que tenemos es una pregunta más general sobre los juegos web. La pregunta dice, ¿qué tan diferente es el proceso entre colocar elementos en una pantalla a través de HTML, CSS, etc., versus hacer uno de estos juegos de navegador HTML5? Y hay una segunda parte en ella. ¿Cómo describirías el actual ecosystem de herramientas y frameworks para construir estos tipos de juegos de navegador? Especialmente esos juegos antiguos retro/barra de juegos. Sí, depende mucho. No estoy seguro de cómo responder a esta pregunta, pero puedes tener un juego regular sin usar Canvas en absoluto si logras. Puede ser más complicado, pero es posible. Normalmente usarías Canvas2D o WebGL para renderizar cosas en tiempo real. ¿Cuál es la segunda parte de la pregunta? Es simplemente cómo describirías las herramientas actuales del ecosistema y los frameworks. Quizás otra forma de formularlo, ¿qué frameworks usas o recomendarías? Actualmente hay muchas opciones, en realidad. Recientemente tuvimos una charla sobre little.js. Es una herramienta que podrías usar. Creo que por lo que vi en esa charla, es principalmente para salidas muy pequeñas, así que usarías eso. Posiblemente podría ser utilizado para esos juegos de anuncios que ves. No estoy realmente seguro. Solo estoy suponiendo que es pequeño. Pero sí, personalmente me gusta usar Pixie.js. Creo que mañana vamos a tener una charla de Play Canvas. Play Canvas también es realmente genial para 3D y tuvimos una charla sobre Babylon. Hay tantas opciones. Es difícil contar, en realidad. Sí. También secundo el Play Canvas porque me gusta. Además, si estás acostumbrado a tener un editor y cosas donde puedes colocar cosas, creo que eso ayuda mucho.
Predicción del lado del cliente e Integración con Unity
La predicción del lado del cliente en juegos multijugador puede ser complicada. Implica ejecutar acciones en el lado del cliente antes de recibir la confirmación del servidor. Este enfoque permite una respuesta inmediata a las acciones del jugador pero puede llevar a desafíos con la colisión de jugador a jugador. Juegos como Brawl Stars y League of Legends no tienen colisión de jugador a jugador debido a su complejidad. Los usuarios de Unity pueden usar Coliseus con un cliente dedicado y hay juegos hechos con Unity que utilizan Coliseus.
Play Canvas hace eso. Genial. Tenemos otra pregunta en general sobre la predicción del lado del cliente y cómo funciona. ¿Puedes hablar un poco sobre eso?
Sí, eso puede ser complicado. Muy recientemente, escribí un cliente solo para movimientos simples. Por ejemplo, también depende de cómo sea tu entrada. Por ejemplo, si estuvieras usando tu teclado como entrada, normalmente enviarías un mensaje en cada fotograma, lo cual puede sonar un poco ridículo, pero así es como funciona. Quiero decir, ¿cómo es un enfoque para esto? Y luego es, realmente no puedo explicarlo correctamente, me faltan palabras, pero básicamente estás intentando ejecutar en el lado del cliente antes de que el servidor realmente envíe el mensaje donde el objeto realmente está en el servidor. Así que tal vez una forma de pensar en ello, porque creo que mencionaste esto un poco en tu charla donde tienes un jugador y el ángulo del jugador y el jugador quiere moverse hacia adelante. Y en lugar de esperar a que el servidor diga, vas a estar en esta posición, puedes asumir que vas a moverte hacia adelante. Y luego cuando lo recibes, puedes decir eso. Sí. Tiene sentido. Y lo que puede ser un poco complicado acerca de esto es que, digamos que mueves tu cliente hacia adelante, pero tienes otro... Por eso es realmente complicado tener una colisión de jugador a jugador en juegos multijugador. Por ejemplo, ves tantos juegos multijugador que no tienen colisión de jugador a jugador porque es complicado. Es complicado. Como, Brawl Stars, como la mayoría de los juegos de apuntar y hacer clic como Dota. Creo que League of Legends tampoco lo tiene, no estoy seguro. Pero tienes tu simulación local, y como, ¿a qué cliente debería confiar este servidor? Es fácil desalinearse. Sí. Genial. No, eso tiene sentido. Eso suena mucho más complicado. Eso obviamente viene con un compromiso de que será, se verá mejor si puedes lograr eso porque entonces tendrás aparentemente menos retraso.
Creo que tenemos una pregunta más. Alguien está preguntando sobre Unity. Si están usando Unity, ¿es posible usar Coliseus? ¿Hay un plugin o una forma de usarlo con un soporte de plugin WebGL de Unity?
Sí, es posible. Hay un cliente disponible para Unity. Y sí, hay un juego que se hizo con Unity.
Disponibilidad de Coliseus y Conclusión
Coliseus está disponible en móviles y se puede encontrar en colossus.io o en el repositorio de GitHub, que proporciona todos los enlaces e información necesarios.
Está disponible en móviles. Es realmente genial. Impresionante. Y si quisieran encontrar eso, ¿estaría en el GitHub o en la página web, el Unity? Sí, está colossus.io. Y desde el repositorio de GitHub, puedes encontrar prácticamente todos los enlaces, toda la información.
Impresionante. Bueno, creo que esas son todas las preguntas que tenemos. Muchas gracias, Endo. Fue genial tenerte aquí. Genial. Muchas gracias por invitarme.
Comments