Chat sencillo usando API multijugador en Godot Engine

Últimamente he estado ayudando un poco a Esbva un desarrollador indie que está haciendo un juego de batallas multijugador online. Ésto me ha llevado a por fin aprender a crear juegos multijugador con Godot Engine.

Este post no es un tutorial sobre cómo aplicar la API multijugador, si no una pequeña aplicación/ejemplo sobre cómo usarlo. Puedes leer la documentación oficial en este link para conocer los aspectos básicos.

Aunque sinceramente para mí no fue lo suficientemente clara esa documentación, aunque lo intenta, este artículo está también altamente referenciado por el tutorial de narwalengineering.com.

Estructuración del proyecto



Tal cual se ve en la imagen, una serie de nodos Control, usaremos un TextEdit para presentar el chat (importante activar la propiedad Readonly), y un LineEdit para que el usuario pueda mandar mensajes, estos dos nodos se llaman ChatDisplay y ChatInput respectivamente.

Los botones LeaveButton, JoinButton, HostButton, IpEnter para controlar el acceso y hosting del chat, colocamos por defecto el text de IpEnter con la IP 127.0.0.1 que es el equivalente a la IP local de tu pc.

Variables y Señales


extends Control

const PORT = 3000
const MAX_USERS = 4 #not including host

onready var chat_display = $RoomUI/ChatDisplay
onready var chat_input = $RoomUI/ChatInput 
func _ready():
    chat_input.connect("text_entered", self, "send_message")
    get_tree().connect("connected_to_server", self, "enter_room")
    get_tree().connect("network_peer_connected", self, "user_entered")
    get_tree().connect("network_peer_disconnected", self, "user_exited")
    get_tree().connect("server_disconnected", self, "_server_disconnected")
    $SetUp/LeaveButton.connect("button_up", self, "leave_room")
    $SetUp/JoinButton.connect("button_up", self, "join_room")
    $SetUp/HostButton.connect("button_up", self, "host_room")
Creo que queda bastante claro cada señal según su nombre, send_message será para enviar mensajes, enter_room se ejecutará cuando un usuario se conecte al servidor, etc...

Inicializando el servidor


La función host_room sería la encargada de esto, escribimos lo siguiente:
func enter_room():
    $SetUp/LeaveButton.show()
    $SetUp/JoinButton.hide()
    $SetUp/IpEnter.hide()
    chat_display.text = "Successfully Joined Room\n"
func host_room():
    var host = NetworkedMultiplayerENet.new()
    host.create_server(PORT, MAX_USERS)
    get_tree().set_network_peer(host)
    enter_room()
    chat_display.text = "Room Created\n"
Para iniciar el servidor debemos crear un objeto NetworkedMultiplayerENet y ejecutar el método create_server con él, luego al árbol de escenas (tree) le pasamos esa red con set_network_peer(), ejecutamos enter_room para adaptar los botones y mostrar en el chat lo sucedido.

Conectando al servidor


Para que un cliente se conecte al servidor necesitamos lo siguiente:


func join_room():
    var ip = $SetUp/IpEnter.text
    var host = NetworkedMultiplayerENet.new()
    host.create_client(ip, PORT)
    get_tree().set_network_peer(host)
Similar al caso de inicializar el servidor, debemos crear un objeto NetworkedMultiplayerENet, en lugar de create_server, debemos ejecutar create_client y pasar por parámetros la IP del servidor y el puerto de comunicación, luego lo pasamos como parámetro al set_network_peer.

Informando la comunicación


func user_entered(id):
    chat_display.text += str(id) + " joined the room\n"

func user_exited(id):
    chat_display.text += str(id) + " left the room\n"

func _server_disconnected():
    chat_display.text += "Disconnected from Server\n"
    leave_room()
Las signals que hemos conectado al inicio nos servirán para obtener si alguien se unió, salió o se desconectó del servidor, la id es única por cada usuario conectado, el servidor siempre tendrá el valor 1.

Abandonando el servidor


func leave_room():
    $SetUp/LeaveButton.hide()
    $SetUp/JoinButton.show()
    $SetUp/HostButton.show()
    $SetUp/IpEnter.show()
    chat_display.text += "Left Room\n"
    get_tree().set_network_peer(null)
No podemos esperar a que el usuario cierre bruscamente el cliente, debemos permitirle poder dejar la sala, lo único que tenemos que hacer es pasar el parámetro null a set_network_peer, lógicamente después de adaptar los botones para que vuelva a su estado inicial.

Enviando y recibiendo mensajes


Para enviar mensajes usaremos la signal text_entered conectado a send_message.

func send_message(msg):
    chat_input.text = ""
    var id = get_tree().get_network_unique_id()
    rpc("receive_message", id, msg)

remotesync func receive_message(id, msg):
    chat_display.text += str(id) + ": " + msg + "\n"

La función rpc (llamadas de procedimiento remoto) es la forma mas sencilla de realizar la comunicación con el servidor, los parámetros son la función a llamar como string, y luego los parámetros que enviaremos a esa función, en este caso id y msg. Recuerda que cada cliente tiene un id único.

Por último la función receive_message lleva una palabra clave remotesync al inicio, esto es para indicar a Godot que esa función puede ser llamada de forma remota y con el servidor, ésto por razones de seguridad. En el caso de solo necesitar que la función solo sea llamada remotamente podemos usar la keyword remote.


Si quieres probar el chat basta con exportar y abrirlo dos veces, crear el servidor con uno y conectarse con el otro, como se muestra en la imagen.

Si quieres probar conexión local, debes obtener la IP de tu configuración de red, con el comando ipconfig en Windows o ifconfig en Linux puedes ver la información necesaria, yo lo he probado creando el host desde Linux y conectandome desde una PC con Windows y funciona excelente.

Te dejo el proyecto de este chat por si lo quieres ojear: Proyecto en Github.

Puedes descargar el proyecto exportado a las siguientes plataformas:

Windows.
Linux.

Comentarios

Publicar un comentario