IcterusGames / NodeWebSockets

A plugin for Godot 4. It gives you two new nodes, a WebSocketServer and a WebSocketClient.
MIT License
17 stars 1 forks source link

Clarification Needed on Setting Up Scripts as Singletons and Handling Ping/Pong Messages #1

Open digimbyte opened 3 months ago

digimbyte commented 3 months ago

I am currently using the NodeWebSockets plugin for Godot and have encountered some challenges. Specifically, I need help with the following:

Setting Up Scripts as Singletons

The documentation is not clear on how to properly set up WebSocketClient and WebSocketServer scripts as singletons. Could you provide a detailed example of how to do this correctly?

Handling Ping/Pong Messages

I am having issues with handling ping (2) and pong (3) messages within the _on_message_received function. The connection frequently closes after receiving a ping message, and I am not sure if I am responding correctly.

Example Code:

Here is a simplified version of my current WebSocketClient script:

extends Node

var socket: WebSocketClient
var url = "ws://localhost:3333/socket.io/?EIO=4&transport=websocket"
var retry_interval = 1
var room_name = ""
var user = "guest"

func _ready():
    connect_to_server()

func connect_to_server():
    socket = WebSocketClient.new()
    socket.connect("connection_established", Callable(self, "_on_connection_established"))
    socket.connect("connection_error", Callable(self, "_on_connection_error"))
    socket.connect("connection_closed", Callable(self, "_on_connection_closed"))
    socket.connect("text_received", Callable(self, "_on_message_received"))
    socket.connect_to_url(url)
    print("Attempting to connect to Socket.IO server...")

func _on_connection_established(peer, protocol):
    print("Connected to Socket.IO server")
    retry_interval = 1
    join_room("lobby")

func _on_connection_error(error):
    print("Failed to connect to WebSocket server. Error:%s" % str(error))
    retry_connection()

func _on_connection_closed(was_clean_close):
    print("Connection to WebSocket server closed. Clean:%s" % str(was_clean_close))
    retry_connection()

func retry_connection():
    var timer = Timer.new()
    timer.set_wait_time(retry_interval)
    timer.set_one_shot(true)
    timer.connect("timeout", Callable(self, "_on_retry_timeout"))
    add_child(timer)
    timer.start()

func _on_retry_timeout():
    retry_interval = min(retry_interval * 2, 60)
    connect_to_server()

func join_room(room: String):
    if socket.is_listening():
        var join_message = {
            "type": "join",
            "room": room,
            "user": user
        }
        socket.send_text("42" + JSON.stringify(join_message))
        room_name = room
        print("Joined room: ", room)
    else:
        print("Socket not connected")

func leave_room():
    if socket.is_listening() and room_name != "":
        var leave_message = {
            "type": "leave",
            "room": room_name
        }
        socket.send_text("42" + JSON.stringify(leave_message))
        print("Left room: ", room_name)
        room_name = ""
    else:
        print("Socket not connected or not in any room")

func _on_message_received(peer, output):
    print("MESSAGE: ", output)
    if output.begins_with("0"):
        var buffer = JSON.new()
        var data = output.substr(1)
        var fault = buffer.parse(data)
        if fault == OK:
            print("JSON OK: ", buffer.data)
            handle_dictionary_message(buffer.data)
        else:
            print("JSON BAD: ", data, JSON.stringify(data))
    elif output.begins_with("42"):
        var buffer = JSON.new()
        var data = output.substr(2)
        var fault = buffer.parse(data)
        if fault == OK:
            print("JSON OK: ", buffer.data)
            handle_custom_event(buffer.data)
        else:
            print("JSON BAD: ", data, JSON.stringify(data))
    elif output == "3":
        print("Pong received")
    elif output == "2":
        print("Ping received, sending pong")
        socket.send_text("3")
    else:
        print("Unexpected message: ", output)

func handle_dictionary_message(data):
    if data.has("type"):
        match data["type"]:
            "lobby_info":
                handle_lobby_info(data)
            "room_update":
                handle_room_update(data)
            "combat_message":
                handle_combat_message(data)
    else:
        print("Unexpected dictionary message: ", data)

func handle_custom_event(data):
    print("Custom event received: ", data)

func handle_lobby_info(data):
    print("Lobby info received: ", data)

func handle_room_update(data):
    print("Room update received: ", data)

func handle_combat_message(data):
    print("Combat message received: ", data)

Simplified Node.js Script

I also need a simplified example of a Node.js server script that can interact with this client. Specifically, how to correctly handle the WebSocket handshake and the ping/pong mechanism.

Example Node.js Server:

const http = require('http');
const express = require('express');
const socketIo = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = socketIo(server, {
    cors: {
        origin: "*", // Adjust according to your needs for security
    },
});

io.on('connection', (socket) => {
    console.log('Client connected: ' + socket.id);

    socket.on('message', (message) => {
        console.log('received: ' + message);
        socket.send('echo: ' + message);
    });

    socket.on('ping', () => {
        console.log('Ping received');
        socket.pong();
    });

    socket.on('pong', () => {
        console.log('Pong received');
    });

    socket.on('disconnect', (reason) => {
        console.log('Client disconnected: ' + reason);
    });
});

server.listen(3333, () => {
    console.log('Socket.IO server is running on ws://localhost:3333');
});

Any guidance or examples would be greatly appreciated.

Thank you!

digimbyte commented 3 months ago

image the current issue is that it keeps creating new clients and no way to keep it open

IcterusGames commented 3 months ago

The way you created WebSocketClient requires you to periodically call the poll function, for example from the _process function, that is the reason why the connection is closed, add the following to your code:

func _process(_delta: float) -> void:
    socket.poll()