Escribir un motor de aventuras de texto multijugador en Node.js: agregar chat a nuestro juego (Parte 4)

 

 

 

  • Clase magistral de tipografía, con Elliot Jay Stocks
  • Patrones de diseño de interfaces inteligentes, vídeo de 10h + formación UX

  • Índice
    1. Volver al plan original
    2. Creando el servidor de chat
    3. Las herramientas para el trabajo
      1. Diseñar un servidor de chat
      2. La implementación
    4. Actualización del código de cliente
      1. ¿Qué sucede al enviar un mensaje de chat?
      2. Revisando los cambios de código
    5. Pensamientos finales
      1. Lecturas adicionales sobre SmashingMag:

    Esta es la parte final de una serie sobre cómo crear tu propio motor de aventuras de texto multijugador. Hoy nos centraremos en agregar soporte de chat al cliente de texto de la parte 3 . Revisaremos el diseño básico de un servidor de chat usando Node.js y socket.io, la interacción básica con la interfaz de usuario y cómo hemos integrado el código de chat en la interfaz de usuario existente.

     

    Cualquier plataforma que permita el juego colaborativo entre personas deberá tener una característica muy particular: la capacidad de que los jugadores (de alguna manera) hablen entre sí. Es exactamente por eso que nuestro motor de aventuras de texto integrado en Node.js no estaría completo sin una forma para que los miembros del grupo puedan comunicarse entre sí. Y como se trata de una aventura de texto , esa forma de comunicación se presentará en forma de una ventana de chat.

    Entonces, en este artículo, explicaré cómo agregué soporte de chat para el cliente de texto y cómo diseñé un servidor de chat rápido usando Node.js.

    Otras partes de esta serie

    • Parte 1 : La Introducción
    • Parte 2 : Diseño del servidor Game Engine
    • Parte 3 : Creación del cliente terminal

    Volver al plan original

    Dejando a un lado la falta de habilidades de diseño, esta ha sido la estructura alámbrica/maqueta original para el cliente basado en texto que construimos en la parte anterior de la serie:

     

    ( Vista previa grande )

    El lado derecho de esa imagen está destinado a las comunicaciones entre jugadores y se ha planeado como un chat desde el principio. Luego, durante el desarrollo de este módulo en particular (el cliente de texto), logré simplificarlo a lo siguiente:

    ( Vista previa grande )

    Sí, ya cubrimos esta imagen en la entrega anterior pero nuestro enfoque fue la mitad izquierda. Hoy, sin embargo, nos centraremos en la mitad derecha de lo que están viendo allí. En otras palabras:

    • Agregar la capacidad de extraer datos de forma reactiva de un servicio de terceros y actualizar una ventana de contenido.
    • Agregar soporte a la interfaz de comando para comandos de chat. Básicamente, cambiar la forma en que funcionan los comandos de forma inmediata y agregar soporte para cosas, como "enviar un mensaje al resto del equipo".
    • Cree un servidor de chat básico en el back-end que pueda facilitar la comunicación del equipo.

    Permítanme comenzar con el último antes de pasar a cómo modificar nuestro código existente.

    Creando el servidor de chat

    Antes incluso de mirar cualquier código, una de las primeras cosas que se deben hacer es definir rápidamente el alcance de cualquier proyecto nuevo. Particularmente con este, debemos asegurarnos de no dedicar mucho tiempo a trabajar en funciones que quizás no necesitemos para nuestro caso de uso particular.

    Verá, todo lo que necesitamos es que los miembros del grupo puedan enviarse mensajes entre sí, pero cuando uno piensa en un "servidor de chat", a menudo vienen a la mente otras características (como salas de chat, mensajes privados, emojis, etc.). en).

    Entonces, para mantener nuestro trabajo manejable y obtener algo que funcione, esto es lo que realmente hará el módulo del servidor de chat:

    • Permita una habitación individual por grupo. Es decir, la sala real para un grupo se creará automáticamente cuando se cree el juego y el primer jugador comience a jugar. Todos los miembros posteriores del grupo se unirán a la misma sala, automáticamente y sin opción.
    • No habrá soporte para mensajes privados. No hay necesidad de ser reservado en tu grupo. Al menos no en esta primera versión. Los usuarios sólo podrán enviar mensajes a través del chat, nada más.
    • Y para asegurarnos de que todos estén al tanto, la única notificación enviada a todo el grupo será cuando nuevos jugadores se unan al juego. Eso es todo.

    El siguiente diagrama muestra la comunicación entre servidores y clientes. Como mencioné, la mecánica es bastante simple, por lo que lo más importante a resaltar aquí es el hecho de que mantenemos las conversaciones dentro de los mismos miembros del grupo:

    ( Vista previa grande )

     

    Las herramientas para el trabajo

    Dadas las restricciones anteriores y el hecho de que todo lo que necesitamos es una conexión directa entre los clientes y el servidor de chat, resolveremos este problema con un socket antiguo. O en otras palabras, la herramienta principal que usaremos es socket.io (tenga en cuenta que hay servicios de terceros que brindan servidores de chat administrados, por ejemplo, pero para los propósitos de esto, ir allí sería el equivalente a matar un mosquito con una escopeta).

    Con socket.io podemos establecer una comunicación bidireccional, en tiempo real y basada en eventos entre el servidor y los clientes. A diferencia de lo que hicimos con el motor del juego, donde publicamos una API REST, la conexión de socket proporciona una forma de comunicación más rápida.

    Que es exactamente lo que necesitamos, una forma rápida de conectar clientes y servidores, intercambiando mensajes y enviando transmisiones entre ellos.

    Diseñar un servidor de chat

    Aunque socket.io es bastante mágico cuando se trata de administración de sockets, no es un servidor de chat completo, aún necesitamos definir cierta lógica para usarlo.

    Para nuestra lista particularmente pequeña de características, el diseño de la lógica interna de nuestro servidor debería verse así:

    • El servidor deberá admitir al menos dos tipos de eventos diferentes:
      1. Mensaje nuevo
        Este es obvio, necesitamos saber cuándo se recibe un mensaje nuevo de un cliente, por lo que necesitaremos soporte para este tipo de evento.
      2. Nuevo usuario unido
        Necesitaremos este solo para asegurarnos de que podemos notificar a todo el grupo cuando un nuevo usuario se una a la sala de chat.
    • Internamente manejaremos salas de chat, aunque ese concepto no será algo público para los clientes. En cambio, todo lo que enviarán es la identificación del juego (la identificación que los jugadores usan para unirse al juego). Con esta identificación usaremos la función de habitaciones de socket.io que maneja habitaciones individuales por nosotros.
    • Debido a cómo funciona socket.io, mantiene abierta una sesión en memoria que se asigna automáticamente al socket creado para cada cliente. En otras palabras, tenemos una variable asignada automáticamente a cada cliente individual donde podemos almacenar información, como nombres de jugadores y sala asignada. Usaremos esta sesión de socket para manejar algunas asociaciones internas de la sala del cliente.

    Una nota sobre las sesiones en memoria

    El almacenamiento en memoria no siempre es la mejor solución. Para este ejemplo en particular, lo utilizaré porque simplifica el trabajo. Dicho esto, una mejora buena y sencilla que podría implementar si quisiera convertir esto en un producto listo para producción sería sustituirlo por una instancia de Redis . De esa manera, mantiene el rendimiento en memoria pero agrega una capa adicional de confiabilidad en caso de que algo salga mal y su proceso finalice.

    Dicho todo esto, permítanme mostrarles la implementación real.

    La implementación

    Aunque el proyecto completo se puede ver en GitHub , el código más relevante se encuentra en el archivo principal ( index.js ):

    // Setup basic express serverlet express = require('express');let config = require("config")let app = express();let server = require('http').createServer(app);let io = require('socket.io')(server);let port = process.env.PORT || config.get('app.port');server.listen(port, () = { console.log('Server listening at port %d', port);});let numUsers = 0;io.on('connection', (socket) = { let addedUser = false; // when the client emits 'new message', this listens and executes socket.on(config.get('chat.events.NEWMSG'), (data, done) = { let room = socket.roomname if(!socket.roomname) { socket.emit(config.get('chat.events.NEWMSG'), "You're not part of a room yet") return done() } // we tell the client to execute 'new message' socket.to(socket.roomname).emit(config.get('chat.events.NEWMSG'), { room: room, username: socket.username, message: data }); done() }); socket.on(config.get('chat.events.JOINROOM'), (data, done) = { console.log("Requesting to join a room: ", data) socket.roomname = data.roomname socket.username = data.username socket.join(data.roomname, _ = { socket.to(data.roomname).emit(config.get('chat.events.NEWMSG'), { username: 'Game server', message: socket.username + ' has joined the party!' }) done(null, {joined: true}) }) }) // when the user disconnects.. perform this socket.on('disconnect', () = { if (addedUser) { --numUsers; // echo globally that this client has left socket.to(socket.roomname).emit('user left', { username: socket.username, numUsers: numUsers }); } });});

    Eso es todo lo que hay para este servidor en particular. Sencillo ¿verdad? Un par de notas: Trámites de notarias un USA para hispanos

     

    1. Estoy usando el módulo de configuración para manejar todas mis constantes. Personalmente me encanta este módulo, simplifica mi vida cada vez que necesito mantener los "números mágicos" fuera de mi código. Entonces, todo, desde la lista de mensajes aceptados hasta el puerto que escuchará el servidor, se almacena y se accede a través de él.
    2. Hay dos eventos principales a los que prestar atención, tal como dije antes.
      • Cuando se recibe un mensaje nuevo, que se puede ver cuando escuchamos config.get('chat.events.NEWMSG'). Este código también garantiza que no intentes enviar un mensaje accidentalmente antes de unirte a una sala. Esto no debería suceder si implementa el cliente de chat correctamente, pero por si acaso, este tipo de comprobaciones siempre son útiles cuando otros escriben a los clientes para sus servicios.
      • Cuando un nuevo usuario se une a una sala. Puedes ver ese evento en el config.get('chat.events.JOINROOM')oyente. En ese caso, todo lo que hacemos es agregar el usuario a la sala (nuevamente, esto lo maneja socket.io, por lo que todo lo que se necesita es una sola línea de código) y luego transmitimos a la sala un mensaje notificando quién acaba de unirse. La clave aquí es que al utilizar la instancia de socket del jugador que se une, la transmisión se enviará a todos en la sala excepto al jugador. Nuevamente, el comportamiento lo proporciona socket.io , por lo que no tenemos que agregarlo.

    Eso es todo lo que hay que hacer con el código del servidor. Ahora revisemos cómo integré el código del lado del cliente en el proyecto del cliente de texto.

     

    Actualización del código de cliente

    Para integrar tanto los comandos del chat como los del juego, el cuadro de entrada en la parte inferior de la pantalla tendrá que analizar la entrada del jugador y decidir qué está intentando hacer.

    La regla es simple: si el jugador intenta enviar un mensaje al grupo, comenzará el comando con la palabra "chat", de lo contrario, no lo hará.

    ¿Qué sucede al enviar un mensaje de chat?

    La siguiente lista de acciones tiene lugar cuando el usuario presiona la tecla ENTER:

    1. Una vez que se encuentra un comando de chat, el código activará una nueva rama, donde se utilizará una biblioteca de cliente de chat y se enviará un nuevo mensaje (emitido a través de la conexión de socket activa) al servidor.
    2. El servidor emitirá el mismo mensaje a todos los demás jugadores de la sala.
    3. Se activará una devolución de llamada (configurada durante el arranque) que escucha nuevos eventos del servidor. Dependiendo del tipo de evento (ya sea un jugador envió un mensaje o un jugador acaba de unirse), mostraremos un mensaje en el cuadro de chat (es decir, el cuadro de texto a la derecha).

    El siguiente diagrama presenta una representación gráfica de los pasos anteriores; Lo ideal es que ayude a visualizar qué componentes intervienen en este proceso:

    ( Vista previa grande )

    Revisando los cambios de código

    Para obtener una lista completa de los cambios y el código completo en funcionamiento, debe consultar el repositorio completo en Github . Aquí, voy a echar un vistazo rápidamente a algunos de los fragmentos de código más relevantes.

    Por ejemplo, al configurar la pantalla principal es donde ahora activamos la conexión con el servidor de chat y donde configuramos la devolución de llamada para actualizar el cuadro de chat (cuadro rojo en la parte superior del diagrama de arriba).

    setUpChatBox: function() { let handler = require(this.elements["chatbox"].meta.handlerPath) handler.handle(this.UI.gamestate, (err, evt) = { if(err) { this.UI.setUpAlert(err) return this.UI.renderScreen() } if(evt.event == config.get('chatserver.commands.JOINROOM')) { this.elements["chatbox"].obj.insertBottom(["::You've joined the party chat room::"]) this.elements["chatbox"].obj.scroll((config.get("screens.main-ui.elements.gamebox.autoscrollspeed") ) + 1) } if(evt.event == config.get('chatserver.commands.SENDMSG')) { this.elements["chatbox"].obj.insertBottom([evt.msg.username + ' said : ' + evt.msg.message]) this.elements["chatbox"].obj.scroll((config.get("screens.main-ui.elements.gamebox.autoscrollspeed") ) + 1) } this.UI.renderScreen() }) },

    Este método se llama desde el método init, como todo lo demás. La función principal de este código es utilizar el controlador asignado (el controlador del cuadro de chat) y llamar a su método de control , que se conectará al servidor de chat y, luego, configurar la devolución de llamada (que también se define aquí) para que se active cuando suceda algo. (uno de los dos eventos que apoyamos).

    La lógica interesante del fragmento anterior está dentro de la devolución de llamada, porque es la lógica utilizada para actualizar el cuadro de chat.

     

    Para completar, el código que se conecta al servidor y configura la devolución de llamada que se muestra arriba es el siguiente:

    const io = require('socket.io-client'), config = require("config"), logger = require("../utils/logger")// Use https or wss in production.let url = config.get("chatserver.url") let socket = io(url)module.exports = { connect2Room: function(gamestate, done) { socket.on(config.get('chatserver.commands.SENDMSG'), msg = { done(null, { event: config.get('chatserver.commands.SENDMSG'), msg: msg }) }) socket.emit(config.get("chatserver.commands.JOINROOM") , { roomname: gamestate.gameID, username: gamestate.playername }, _ = { logger.info("Room joined!") gamestate.inroom = true done(null, { event: config.get('chatserver.commands.JOINROOM') }) }) }, handleCommand: function(command, gamestate, done) { logger.info("Sending command to chatserver!") let message = command.split(" ").splice(1).join(" ") logger.info("Message to send: ", message) if(!gamestate.inroom) { //first time sending the message, so join the room first logger.info("Joining a room") let gameId = gamestate.game socket.emit(config.get("chatserver.commands.JOINROOM") , { roomname: gamestate.gameID, username: gamestate.playername }, _ = { logger.info("Room joined!") gamestate.inroom = true updateGameState = true logger.info("Updating game state ...") socket.emit(config.get("chatserver.commands.SENDMSG"), message, done) }) } else { logger.info("Sending message to chat server: ", message ) socket.emit(config.get("chatserver.commands.SENDMSG"), message, done) } }}

    El connect2roommétodo es el que se llama durante la configuración de la pantalla principal como mencioné, puedes ver cómo configuramos el controlador para mensajes nuevos y emitimos el evento relacionado con unirse a una sala (que luego activa la transmisión del mismo evento a otros jugadores en el lado del servidor).

    El otro método, handleCommandes el que se encarga de enviar el mensaje del chat al servidor (y lo hace con un simple socket.emit). Éste se ejecuta cuando se commandHandlerda cuenta de que se está enviando un mensaje de chat. Aquí está el código para esa lógica:

    module.exports = { handle: function(gamestate, text, done) { let command = text.trim() if(command.indexOf("chat") === 0) { //chat command chatServerClient.handleCommand(command, gamestate, done) } else { sendGameCommand(gamestate, text, done) } }}

    Ese es el nuevo código para CommandHandler, la función sendGameCommand es donde ahora está encapsulado el código antiguo (nada cambió allí).

    Y eso es todo en cuanto a la integración, nuevamente, el código completamente funcional se puede descargar y probar desde el repositorio completo .

    Pensamientos finales

    Esto marca el final del camino para este proyecto. Si te apegaste hasta el final, ¡gracias por leer! El código está listo para ser probado y jugado, y si lo hace, comuníquese con nosotros y cuénteme qué pensó al respecto.

    Con suerte, con este proyecto, muchos fanáticos del género de antaño puedan volver a él y experimentarlo como nunca lo hicieron.

    ¡Diviértete jugando (y codificando)!

    Lecturas adicionales sobre SmashingMag:

    • Creación de un servicio Pub/Sub interno utilizando Node.js y Redis
    • Creación de una API Node.js Express para convertir Markdown a HTML
    • Comience con Node: una introducción a las API, HTTP y JavaScript ES6+
    • Mantener Node.js rápido: herramientas, técnicas y consejos para crear servidores Node.js de alto rendimiento

    (dm, yk, il)Explora más en

    • Nodo.js
    • javascript





    Tal vez te puede interesar:

    1. ¿Deberían abrirse los enlaces en ventanas nuevas?
    2. 24 excelentes tutoriales de AJAX
    3. 70 técnicas nuevas y útiles de AJAX y JavaScript
    4. Más de 45 excelentes recursos y repositorios de fragmentos de código

    Escribir un motor de aventuras de texto multijugador en Node.js: agregar chat a nuestro juego (Parte 4)

    Escribir un motor de aventuras de texto multijugador en Node.js: agregar chat a nuestro juego (Parte 4)

    Clase magistral de tipografía, con Elliot Jay Stocks Patrones de diseño de interfaces inteligentes, vídeo de 10h + formación UX Índice

    programar

    es

    https://pseint.es/static/images/programar-escribir-un-motor-de-aventuras-de-texto-multijugador-en-node-1007-0.jpg

    2024-04-04

     

    Escribir un motor de aventuras de texto multijugador en Node.js: agregar chat a nuestro juego (Parte 4)
    Escribir un motor de aventuras de texto multijugador en Node.js: agregar chat a nuestro juego (Parte 4)

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20